Merge branch 'master' into wip_leon_T7456_New_Customer_Portal_100_Report
[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 || this.summaryFooterShow){
9422                 cfg.cn.push(this.renderFooter());
9423             }
9424
9425             // where does this come from?
9426             //cfg.cls+=  ' TableGrid';
9427         }
9428         
9429         return { cn : [ cfg ] };
9430     },
9431     
9432     initEvents : function()
9433     {   
9434         if(!this.store || !this.cm){
9435             return;
9436         }
9437         if (this.selModel) {
9438             this.selModel.initEvents();
9439         }
9440         
9441         
9442         //Roo.log('initEvents with ds!!!!');
9443         
9444         this.bodyEl = this.el.select('tbody', true).first();
9445         this.headEl = this.el.select('thead', true).first();
9446         this.mainFoot = this.el.select('tfoot', true).first();
9447         
9448         
9449         
9450         
9451         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9452             e.on('click', this.sort, this);
9453         }, this);
9454         
9455         
9456         // why is this done????? = it breaks dialogs??
9457         //this.parent().el.setStyle('position', 'relative');
9458         
9459         
9460         if (this.footer) {
9461             this.footer.parentId = this.id;
9462             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9463             
9464             if(this.lazyLoad){
9465                 this.el.select('tfoot tr td').first().addClass('hide');
9466             }
9467         } 
9468         
9469         if(this.loadMask) {
9470             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9471         }
9472         
9473         this.store.on('load', this.onLoad, this);
9474         this.store.on('beforeload', this.onBeforeLoad, this);
9475         this.store.on('update', this.onUpdate, this);
9476         this.store.on('add', this.onAdd, this);
9477         this.store.on("clear", this.clear, this);
9478         
9479         this.el.on("contextmenu", this.onContextMenu, this);
9480         
9481         
9482         this.cm.on("headerchange", this.onHeaderChange, this);
9483         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9484
9485  //?? does bodyEl get replaced on render?
9486         this.bodyEl.on("click", this.onClick, this);
9487         this.bodyEl.on("dblclick", this.onDblClick, this);        
9488         this.bodyEl.on('scroll', this.onBodyScroll, this);
9489
9490         // guessing mainbody will work - this relays usually caught by selmodel at present.
9491         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9492   
9493   
9494         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9495         
9496   
9497         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9498             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9499         }
9500         
9501         this.initCSS();
9502     },
9503     // Compatibility with grid - we implement all the view features at present.
9504     getView : function()
9505     {
9506         return this;
9507     },
9508     
9509     initCSS : function()
9510     {
9511         if(this.disableAutoSize) {
9512             return;
9513         }
9514         
9515         var cm = this.cm, styles = [];
9516         this.CSS.removeStyleSheet(this.id + '-cssrules');
9517         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9518         // we can honour xs/sm/md/xl  as widths...
9519         // we first have to decide what widht we are currently at...
9520         var sz = Roo.getGridSize();
9521         
9522         var total = 0;
9523         var last = -1;
9524         var cols = []; // visable cols.
9525         var total_abs = 0;
9526         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9527             var w = cm.getColumnWidth(i, false);
9528             if(cm.isHidden(i)){
9529                 cols.push( { rel : false, abs : 0 });
9530                 continue;
9531             }
9532             if (w !== false) {
9533                 cols.push( { rel : false, abs : w });
9534                 total_abs += w;
9535                 last = i; // not really..
9536                 continue;
9537             }
9538             var w = cm.getColumnWidth(i, sz);
9539             if (w > 0) {
9540                 last = i
9541             }
9542             total += w;
9543             cols.push( { rel : w, abs : false });
9544         }
9545         
9546         var avail = this.bodyEl.dom.clientWidth - total_abs;
9547         
9548         var unitWidth = Math.floor(avail / total);
9549         var rem = avail - (unitWidth * total);
9550         
9551         var hidden, width, pos = 0 , splithide , left;
9552         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9553             
9554             hidden = 'display:none;';
9555             left = '';
9556             width  = 'width:0px;';
9557             splithide = '';
9558             if(!cm.isHidden(i)){
9559                 hidden = '';
9560                 
9561                 
9562                 // we can honour xs/sm/md/xl ?
9563                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9564                 if (w===0) {
9565                     hidden = 'display:none;';
9566                 }
9567                 // width should return a small number...
9568                 if (i == last) {
9569                     w+=rem; // add the remaining with..
9570                 }
9571                 pos += w;
9572                 left = "left:" + (pos -4) + "px;";
9573                 width = "width:" + w+ "px;";
9574                 
9575             }
9576             if (this.responsive) {
9577                 width = '';
9578                 left = '';
9579                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9580                 splithide = 'display: none;';
9581             }
9582             
9583             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9584             if (this.headEl) {
9585                 if (i == last) {
9586                     splithide = 'display:none;';
9587                 }
9588                 
9589                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9590                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9591                             // this is the popover version..
9592                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9593                 );
9594             }
9595             
9596         }
9597         //Roo.log(styles.join(''));
9598         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9599         
9600     },
9601     
9602     
9603     
9604     onContextMenu : function(e, t)
9605     {
9606         this.processEvent("contextmenu", e);
9607     },
9608     
9609     processEvent : function(name, e)
9610     {
9611         if (name != 'touchstart' ) {
9612             this.fireEvent(name, e);    
9613         }
9614         
9615         var t = e.getTarget();
9616         
9617         var cell = Roo.get(t);
9618         
9619         if(!cell){
9620             return;
9621         }
9622         
9623         if(cell.findParent('tfoot', false, true)){
9624             return;
9625         }
9626         
9627         if(cell.findParent('thead', false, true)){
9628             
9629             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9630                 cell = Roo.get(t).findParent('th', false, true);
9631                 if (!cell) {
9632                     Roo.log("failed to find th in thead?");
9633                     Roo.log(e.getTarget());
9634                     return;
9635                 }
9636             }
9637             
9638             var cellIndex = cell.dom.cellIndex;
9639             
9640             var ename = name == 'touchstart' ? 'click' : name;
9641             this.fireEvent("header" + ename, this, cellIndex, e);
9642             
9643             return;
9644         }
9645         
9646         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9647             cell = Roo.get(t).findParent('td', false, true);
9648             if (!cell) {
9649                 Roo.log("failed to find th in tbody?");
9650                 Roo.log(e.getTarget());
9651                 return;
9652             }
9653         }
9654         
9655         var row = cell.findParent('tr', false, true);
9656         var cellIndex = cell.dom.cellIndex;
9657         var rowIndex = row.dom.rowIndex - 1;
9658         
9659         if(row !== false){
9660             
9661             this.fireEvent("row" + name, this, rowIndex, e);
9662             
9663             if(cell !== false){
9664             
9665                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9666             }
9667         }
9668         
9669     },
9670     
9671     onMouseover : function(e, el)
9672     {
9673         var cell = Roo.get(el);
9674         
9675         if(!cell){
9676             return;
9677         }
9678         
9679         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9680             cell = cell.findParent('td', false, true);
9681         }
9682         
9683         var row = cell.findParent('tr', false, true);
9684         var cellIndex = cell.dom.cellIndex;
9685         var rowIndex = row.dom.rowIndex - 1; // start from 0
9686         
9687         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9688         
9689     },
9690     
9691     onMouseout : function(e, el)
9692     {
9693         var cell = Roo.get(el);
9694         
9695         if(!cell){
9696             return;
9697         }
9698         
9699         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9700             cell = cell.findParent('td', false, true);
9701         }
9702         
9703         var row = cell.findParent('tr', false, true);
9704         var cellIndex = cell.dom.cellIndex;
9705         var rowIndex = row.dom.rowIndex - 1; // start from 0
9706         
9707         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9708         
9709     },
9710     
9711     onClick : function(e, el)
9712     {
9713         var cell = Roo.get(el);
9714         
9715         if(!cell || (!this.cellSelection && !this.rowSelection)){
9716             return;
9717         }
9718         
9719         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9720             cell = cell.findParent('td', false, true);
9721         }
9722         
9723         if(!cell || typeof(cell) == 'undefined'){
9724             return;
9725         }
9726         
9727         var row = cell.findParent('tr', false, true);
9728         
9729         if(!row || typeof(row) == 'undefined'){
9730             return;
9731         }
9732         
9733         var cellIndex = cell.dom.cellIndex;
9734         var rowIndex = this.getRowIndex(row);
9735         
9736         // why??? - should these not be based on SelectionModel?
9737         //if(this.cellSelection){
9738             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9739         //}
9740         
9741         //if(this.rowSelection){
9742             this.fireEvent('rowclick', this, row, rowIndex, e);
9743         //}
9744          
9745     },
9746         
9747     onDblClick : function(e,el)
9748     {
9749         var cell = Roo.get(el);
9750         
9751         if(!cell || (!this.cellSelection && !this.rowSelection)){
9752             return;
9753         }
9754         
9755         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9756             cell = cell.findParent('td', false, true);
9757         }
9758         
9759         if(!cell || typeof(cell) == 'undefined'){
9760             return;
9761         }
9762         
9763         var row = cell.findParent('tr', false, true);
9764         
9765         if(!row || typeof(row) == 'undefined'){
9766             return;
9767         }
9768         
9769         var cellIndex = cell.dom.cellIndex;
9770         var rowIndex = this.getRowIndex(row);
9771         
9772         if(this.cellSelection){
9773             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9774         }
9775         
9776         if(this.rowSelection){
9777             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9778         }
9779     },
9780     findRowIndex : function(el)
9781     {
9782         var cell = Roo.get(el);
9783         if(!cell) {
9784             return false;
9785         }
9786         var row = cell.findParent('tr', false, true);
9787         
9788         if(!row || typeof(row) == 'undefined'){
9789             return false;
9790         }
9791         return this.getRowIndex(row);
9792     },
9793     sort : function(e,el)
9794     {
9795         var col = Roo.get(el);
9796         
9797         if(!col.hasClass('sortable')){
9798             return;
9799         }
9800         
9801         var sort = col.attr('sort');
9802         var dir = 'ASC';
9803         
9804         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9805             dir = 'DESC';
9806         }
9807         
9808         this.store.sortInfo = {field : sort, direction : dir};
9809         
9810         if (this.footer) {
9811             Roo.log("calling footer first");
9812             this.footer.onClick('first');
9813         } else {
9814         
9815             this.store.load({ params : { start : 0 } });
9816         }
9817     },
9818     
9819     renderHeader : function()
9820     {
9821         var header = {
9822             tag: 'thead',
9823             cn : []
9824         };
9825         
9826         var cm = this.cm;
9827         this.totalWidth = 0;
9828         
9829         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9830             
9831             var config = cm.config[i];
9832             
9833             var c = {
9834                 tag: 'th',
9835                 cls : 'x-hcol-' + i,
9836                 style : '',
9837                 
9838                 html: cm.getColumnHeader(i)
9839             };
9840             
9841             var tooltip = cm.getColumnTooltip(i);
9842             if (tooltip) {
9843                 c.tooltip = tooltip;
9844             }
9845             
9846             
9847             var hh = '';
9848             
9849             if(typeof(config.sortable) != 'undefined' && config.sortable){
9850                 c.cls += ' sortable';
9851                 c.html = '<i class="fa"></i>' + c.html;
9852             }
9853             
9854             // could use BS4 hidden-..-down 
9855             
9856             if(typeof(config.lgHeader) != 'undefined'){
9857                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9858             }
9859             
9860             if(typeof(config.mdHeader) != 'undefined'){
9861                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9862             }
9863             
9864             if(typeof(config.smHeader) != 'undefined'){
9865                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9866             }
9867             
9868             if(typeof(config.xsHeader) != 'undefined'){
9869                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9870             }
9871             
9872             if(hh.length){
9873                 c.html = hh;
9874             }
9875             
9876             if(typeof(config.tooltip) != 'undefined'){
9877                 c.tooltip = config.tooltip;
9878             }
9879             
9880             if(typeof(config.colspan) != 'undefined'){
9881                 c.colspan = config.colspan;
9882             }
9883             
9884             // hidden is handled by CSS now
9885             
9886             if(typeof(config.dataIndex) != 'undefined'){
9887                 c.sort = config.dataIndex;
9888             }
9889             
9890            
9891             
9892             if(typeof(config.align) != 'undefined' && config.align.length){
9893                 c.style += ' text-align:' + config.align + ';';
9894             }
9895             
9896             /* width is done in CSS
9897              *if(typeof(config.width) != 'undefined'){
9898                 c.style += ' width:' + config.width + 'px;';
9899                 this.totalWidth += config.width;
9900             } else {
9901                 this.totalWidth += 100; // assume minimum of 100 per column?
9902             }
9903             */
9904             
9905             if(typeof(config.cls) != 'undefined'){
9906                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9907             }
9908             // this is the bit that doesnt reall work at all...
9909             
9910             if (this.responsive) {
9911                  
9912             
9913                 ['xs','sm','md','lg'].map(function(size){
9914                     
9915                     if(typeof(config[size]) == 'undefined'){
9916                         return;
9917                     }
9918                      
9919                     if (!config[size]) { // 0 = hidden
9920                         // BS 4 '0' is treated as hide that column and below.
9921                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9922                         return;
9923                     }
9924                     
9925                     c.cls += ' col-' + size + '-' + config[size] + (
9926                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9927                     );
9928                     
9929                     
9930                 });
9931             }
9932             // at the end?
9933             
9934             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9935             
9936             
9937             
9938             
9939             header.cn.push(c)
9940         }
9941         
9942         return header;
9943     },
9944     
9945     renderBody : function()
9946     {
9947         var body = {
9948             tag: 'tbody',
9949             cn : [
9950                 {
9951                     tag: 'tr',
9952                     cn : [
9953                         {
9954                             tag : 'td',
9955                             colspan :  this.cm.getColumnCount()
9956                         }
9957                     ]
9958                 }
9959             ]
9960         };
9961         
9962         return body;
9963     },
9964     
9965     renderFooter : function()
9966     {
9967         var footer = {
9968             tag: 'tfoot',
9969             cn : [
9970                 {
9971                     tag: 'tr',
9972                     cn : [
9973                         {
9974                             tag : 'td',
9975                             colspan :  this.cm.getColumnCount()
9976                         }
9977                     ]
9978                 }
9979             ]
9980         };
9981         
9982         return footer;
9983     },
9984     
9985     onLoad : function()
9986     {
9987 //        Roo.log('ds onload');
9988         this.clear();
9989         
9990         var _this = this;
9991         var cm = this.cm;
9992         var ds = this.store;
9993         
9994         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9995             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9996             if (_this.store.sortInfo) {
9997                     
9998                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9999                     e.select('i', true).addClass(['fa-arrow-up']);
10000                 }
10001                 
10002                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10003                     e.select('i', true).addClass(['fa-arrow-down']);
10004                 }
10005             }
10006         });
10007         
10008         var tbody =  this.bodyEl;
10009               
10010         if(ds.getCount() > 0){
10011             ds.data.each(function(d,rowIndex){
10012                 var row =  this.renderRow(cm, ds, rowIndex);
10013                 
10014                 tbody.createChild(row);
10015                 
10016                 var _this = this;
10017                 
10018                 if(row.cellObjects.length){
10019                     Roo.each(row.cellObjects, function(r){
10020                         _this.renderCellObject(r);
10021                     })
10022                 }
10023                 
10024             }, this);
10025         } else if (this.empty_results.length) {
10026             this.el.mask(this.empty_results, 'no-spinner');
10027         }
10028         
10029         var tfoot = this.el.select('tfoot', true).first();
10030         
10031         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10032             
10033             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10034             
10035             var total = this.ds.getTotalCount();
10036             
10037             if(this.footer.pageSize < total){
10038                 this.mainFoot.show();
10039             }
10040         }
10041
10042         if(!this.footerShow && this.summaryFooterShow) {
10043
10044             var tr = {
10045                 tag : 'tr',
10046                 cn : []
10047             };
10048
10049             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10050         
10051                 var value = cm.config[i].summaryFooter;
10052
10053                 var td = {
10054                     tag: 'td',
10055                     cls : ' x-fcol-' + i,
10056                     style: '',
10057                     html: cm.config[i].summaryFooter
10058                 };
10059
10060                 tr.cn.push(td);
10061                 
10062             }
10063             
10064             tfoot.dom.innerHTML = '';
10065
10066             tfoot.createChild(tr);
10067         }
10068         
10069         Roo.each(this.el.select('tbody td', true).elements, function(e){
10070             e.on('mouseover', _this.onMouseover, _this);
10071         });
10072         
10073         Roo.each(this.el.select('tbody td', true).elements, function(e){
10074             e.on('mouseout', _this.onMouseout, _this);
10075         });
10076         this.fireEvent('rowsrendered', this);
10077         
10078         this.autoSize();
10079         
10080         this.initCSS(); /// resize cols
10081
10082         
10083     },
10084     
10085     
10086     onUpdate : function(ds,record)
10087     {
10088         this.refreshRow(record);
10089         this.autoSize();
10090     },
10091     
10092     onRemove : function(ds, record, index, isUpdate){
10093         if(isUpdate !== true){
10094             this.fireEvent("beforerowremoved", this, index, record);
10095         }
10096         var bt = this.bodyEl.dom;
10097         
10098         var rows = this.el.select('tbody > tr', true).elements;
10099         
10100         if(typeof(rows[index]) != 'undefined'){
10101             bt.removeChild(rows[index].dom);
10102         }
10103         
10104 //        if(bt.rows[index]){
10105 //            bt.removeChild(bt.rows[index]);
10106 //        }
10107         
10108         if(isUpdate !== true){
10109             //this.stripeRows(index);
10110             //this.syncRowHeights(index, index);
10111             //this.layout();
10112             this.fireEvent("rowremoved", this, index, record);
10113         }
10114     },
10115     
10116     onAdd : function(ds, records, rowIndex)
10117     {
10118         //Roo.log('on Add called');
10119         // - note this does not handle multiple adding very well..
10120         var bt = this.bodyEl.dom;
10121         for (var i =0 ; i < records.length;i++) {
10122             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10123             //Roo.log(records[i]);
10124             //Roo.log(this.store.getAt(rowIndex+i));
10125             this.insertRow(this.store, rowIndex + i, false);
10126             return;
10127         }
10128         
10129     },
10130     
10131     
10132     refreshRow : function(record){
10133         var ds = this.store, index;
10134         if(typeof record == 'number'){
10135             index = record;
10136             record = ds.getAt(index);
10137         }else{
10138             index = ds.indexOf(record);
10139             if (index < 0) {
10140                 return; // should not happen - but seems to 
10141             }
10142         }
10143         this.insertRow(ds, index, true);
10144         this.autoSize();
10145         this.onRemove(ds, record, index+1, true);
10146         this.autoSize();
10147         //this.syncRowHeights(index, index);
10148         //this.layout();
10149         this.fireEvent("rowupdated", this, index, record);
10150     },
10151     // private - called by RowSelection
10152     onRowSelect : function(rowIndex){
10153         var row = this.getRowDom(rowIndex);
10154         row.addClass(['bg-info','info']);
10155     },
10156     // private - called by RowSelection
10157     onRowDeselect : function(rowIndex)
10158     {
10159         if (rowIndex < 0) {
10160             return;
10161         }
10162         var row = this.getRowDom(rowIndex);
10163         row.removeClass(['bg-info','info']);
10164     },
10165       /**
10166      * Focuses the specified row.
10167      * @param {Number} row The row index
10168      */
10169     focusRow : function(row)
10170     {
10171         //Roo.log('GridView.focusRow');
10172         var x = this.bodyEl.dom.scrollLeft;
10173         this.focusCell(row, 0, false);
10174         this.bodyEl.dom.scrollLeft = x;
10175
10176     },
10177      /**
10178      * Focuses the specified cell.
10179      * @param {Number} row The row index
10180      * @param {Number} col The column index
10181      * @param {Boolean} hscroll false to disable horizontal scrolling
10182      */
10183     focusCell : function(row, col, hscroll)
10184     {
10185         //Roo.log('GridView.focusCell');
10186         var el = this.ensureVisible(row, col, hscroll);
10187         // not sure what focusEL achives = it's a <a> pos relative 
10188         //this.focusEl.alignTo(el, "tl-tl");
10189         //if(Roo.isGecko){
10190         //    this.focusEl.focus();
10191         //}else{
10192         //    this.focusEl.focus.defer(1, this.focusEl);
10193         //}
10194     },
10195     
10196      /**
10197      * Scrolls the specified cell into view
10198      * @param {Number} row The row index
10199      * @param {Number} col The column index
10200      * @param {Boolean} hscroll false to disable horizontal scrolling
10201      */
10202     ensureVisible : function(row, col, hscroll)
10203     {
10204         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10205         //return null; //disable for testing.
10206         if(typeof row != "number"){
10207             row = row.rowIndex;
10208         }
10209         if(row < 0 && row >= this.ds.getCount()){
10210             return  null;
10211         }
10212         col = (col !== undefined ? col : 0);
10213         var cm = this.cm;
10214         while(cm.isHidden(col)){
10215             col++;
10216         }
10217
10218         var el = this.getCellDom(row, col);
10219         if(!el){
10220             return null;
10221         }
10222         var c = this.bodyEl.dom;
10223
10224         var ctop = parseInt(el.offsetTop, 10);
10225         var cleft = parseInt(el.offsetLeft, 10);
10226         var cbot = ctop + el.offsetHeight;
10227         var cright = cleft + el.offsetWidth;
10228
10229         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10230         var ch = 0; //?? header is not withing the area?
10231         var stop = parseInt(c.scrollTop, 10);
10232         var sleft = parseInt(c.scrollLeft, 10);
10233         var sbot = stop + ch;
10234         var sright = sleft + c.clientWidth;
10235         /*
10236         Roo.log('GridView.ensureVisible:' +
10237                 ' ctop:' + ctop +
10238                 ' c.clientHeight:' + c.clientHeight +
10239                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10240                 ' stop:' + stop +
10241                 ' cbot:' + cbot +
10242                 ' sbot:' + sbot +
10243                 ' ch:' + ch  
10244                 );
10245         */
10246         if(ctop < stop){
10247             c.scrollTop = ctop;
10248             //Roo.log("set scrolltop to ctop DISABLE?");
10249         }else if(cbot > sbot){
10250             //Roo.log("set scrolltop to cbot-ch");
10251             c.scrollTop = cbot-ch;
10252         }
10253
10254         if(hscroll !== false){
10255             if(cleft < sleft){
10256                 c.scrollLeft = cleft;
10257             }else if(cright > sright){
10258                 c.scrollLeft = cright-c.clientWidth;
10259             }
10260         }
10261
10262         return el;
10263     },
10264     
10265     
10266     insertRow : function(dm, rowIndex, isUpdate){
10267         
10268         if(!isUpdate){
10269             this.fireEvent("beforerowsinserted", this, rowIndex);
10270         }
10271             //var s = this.getScrollState();
10272         var row = this.renderRow(this.cm, this.store, rowIndex);
10273         // insert before rowIndex..
10274         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10275         
10276         var _this = this;
10277                 
10278         if(row.cellObjects.length){
10279             Roo.each(row.cellObjects, function(r){
10280                 _this.renderCellObject(r);
10281             })
10282         }
10283             
10284         if(!isUpdate){
10285             this.fireEvent("rowsinserted", this, rowIndex);
10286             //this.syncRowHeights(firstRow, lastRow);
10287             //this.stripeRows(firstRow);
10288             //this.layout();
10289         }
10290         
10291     },
10292     
10293     
10294     getRowDom : function(rowIndex)
10295     {
10296         var rows = this.el.select('tbody > tr', true).elements;
10297         
10298         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10299         
10300     },
10301     getCellDom : function(rowIndex, colIndex)
10302     {
10303         var row = this.getRowDom(rowIndex);
10304         if (row === false) {
10305             return false;
10306         }
10307         var cols = row.select('td', true).elements;
10308         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10309         
10310     },
10311     
10312     // returns the object tree for a tr..
10313   
10314     
10315     renderRow : function(cm, ds, rowIndex) 
10316     {
10317         var d = ds.getAt(rowIndex);
10318         
10319         var row = {
10320             tag : 'tr',
10321             cls : 'x-row-' + rowIndex,
10322             cn : []
10323         };
10324             
10325         var cellObjects = [];
10326         
10327         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10328             var config = cm.config[i];
10329             
10330             var renderer = cm.getRenderer(i);
10331             var value = '';
10332             var id = false;
10333             
10334             if(typeof(renderer) !== 'undefined'){
10335                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10336             }
10337             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10338             // and are rendered into the cells after the row is rendered - using the id for the element.
10339             
10340             if(typeof(value) === 'object'){
10341                 id = Roo.id();
10342                 cellObjects.push({
10343                     container : id,
10344                     cfg : value 
10345                 })
10346             }
10347             
10348             var rowcfg = {
10349                 record: d,
10350                 rowIndex : rowIndex,
10351                 colIndex : i,
10352                 rowClass : ''
10353             };
10354
10355             this.fireEvent('rowclass', this, rowcfg);
10356             
10357             var td = {
10358                 tag: 'td',
10359                 // this might end up displaying HTML?
10360                 // this is too messy... - better to only do it on columsn you know are going to be too long
10361                 //tooltip : (typeof(value) === 'object') ? '' : value,
10362                 cls : rowcfg.rowClass + ' x-col-' + i,
10363                 style: '',
10364                 html: (typeof(value) === 'object') ? '' : value
10365             };
10366             
10367             if (id) {
10368                 td.id = id;
10369             }
10370             
10371             if(typeof(config.colspan) != 'undefined'){
10372                 td.colspan = config.colspan;
10373             }
10374             
10375             
10376             
10377             if(typeof(config.align) != 'undefined' && config.align.length){
10378                 td.style += ' text-align:' + config.align + ';';
10379             }
10380             if(typeof(config.valign) != 'undefined' && config.valign.length){
10381                 td.style += ' vertical-align:' + config.valign + ';';
10382             }
10383             /*
10384             if(typeof(config.width) != 'undefined'){
10385                 td.style += ' width:' +  config.width + 'px;';
10386             }
10387             */
10388             
10389             if(typeof(config.cursor) != 'undefined'){
10390                 td.style += ' cursor:' +  config.cursor + ';';
10391             }
10392             
10393             if(typeof(config.cls) != 'undefined'){
10394                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10395             }
10396             if (this.responsive) {
10397                 ['xs','sm','md','lg'].map(function(size){
10398                     
10399                     if(typeof(config[size]) == 'undefined'){
10400                         return;
10401                     }
10402                     
10403                     
10404                       
10405                     if (!config[size]) { // 0 = hidden
10406                         // BS 4 '0' is treated as hide that column and below.
10407                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10408                         return;
10409                     }
10410                     
10411                     td.cls += ' col-' + size + '-' + config[size] + (
10412                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10413                     );
10414                      
10415     
10416                 });
10417             }
10418             row.cn.push(td);
10419            
10420         }
10421         
10422         row.cellObjects = cellObjects;
10423         
10424         return row;
10425           
10426     },
10427     
10428     
10429     
10430     onBeforeLoad : function()
10431     {
10432         this.el.unmask(); // if needed.
10433     },
10434      /**
10435      * Remove all rows
10436      */
10437     clear : function()
10438     {
10439         this.el.select('tbody', true).first().dom.innerHTML = '';
10440     },
10441     /**
10442      * Show or hide a row.
10443      * @param {Number} rowIndex to show or hide
10444      * @param {Boolean} state hide
10445      */
10446     setRowVisibility : function(rowIndex, state)
10447     {
10448         var bt = this.bodyEl.dom;
10449         
10450         var rows = this.el.select('tbody > tr', true).elements;
10451         
10452         if(typeof(rows[rowIndex]) == 'undefined'){
10453             return;
10454         }
10455         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10456         
10457     },
10458     
10459     
10460     getSelectionModel : function(){
10461         if(!this.selModel){
10462             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10463         }
10464         return this.selModel;
10465     },
10466     /*
10467      * Render the Roo.bootstrap object from renderder
10468      */
10469     renderCellObject : function(r)
10470     {
10471         var _this = this;
10472         
10473         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10474         
10475         var t = r.cfg.render(r.container);
10476         
10477         if(r.cfg.cn){
10478             Roo.each(r.cfg.cn, function(c){
10479                 var child = {
10480                     container: t.getChildContainer(),
10481                     cfg: c
10482                 };
10483                 _this.renderCellObject(child);
10484             })
10485         }
10486     },
10487     /**
10488      * get the Row Index from a dom element.
10489      * @param {Roo.Element} row The row to look for
10490      * @returns {Number} the row
10491      */
10492     getRowIndex : function(row)
10493     {
10494         var rowIndex = -1;
10495         
10496         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10497             if(el != row){
10498                 return;
10499             }
10500             
10501             rowIndex = index;
10502         });
10503         
10504         return rowIndex;
10505     },
10506     /**
10507      * get the header TH element for columnIndex
10508      * @param {Number} columnIndex
10509      * @returns {Roo.Element}
10510      */
10511     getHeaderIndex: function(colIndex)
10512     {
10513         var cols = this.headEl.select('th', true).elements;
10514         return cols[colIndex]; 
10515     },
10516     /**
10517      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10518      * @param {domElement} cell to look for
10519      * @returns {Number} the column
10520      */
10521     getCellIndex : function(cell)
10522     {
10523         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10524         if(id){
10525             return parseInt(id[1], 10);
10526         }
10527         return 0;
10528     },
10529      /**
10530      * Returns the grid's underlying element = used by panel.Grid
10531      * @return {Element} The element
10532      */
10533     getGridEl : function(){
10534         return this.el;
10535     },
10536      /**
10537      * Forces a resize - used by panel.Grid
10538      * @return {Element} The element
10539      */
10540     autoSize : function()
10541     {
10542         if(this.disableAutoSize) {
10543             return;
10544         }
10545         //var ctr = Roo.get(this.container.dom.parentElement);
10546         var ctr = Roo.get(this.el.dom);
10547         
10548         var thd = this.getGridEl().select('thead',true).first();
10549         var tbd = this.getGridEl().select('tbody', true).first();
10550         var tfd = this.getGridEl().select('tfoot', true).first();
10551         
10552         var cw = ctr.getWidth();
10553         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10554         
10555         if (tbd) {
10556             
10557             tbd.setWidth(ctr.getWidth());
10558             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10559             // this needs fixing for various usage - currently only hydra job advers I think..
10560             //tdb.setHeight(
10561             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10562             //); 
10563             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10564             cw -= barsize;
10565         }
10566         cw = Math.max(cw, this.totalWidth);
10567         this.getGridEl().select('tbody tr',true).setWidth(cw);
10568         this.initCSS();
10569         
10570         // resize 'expandable coloumn?
10571         
10572         return; // we doe not have a view in this design..
10573         
10574     },
10575     onBodyScroll: function()
10576     {
10577         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10578         if(this.headEl){
10579             this.headEl.setStyle({
10580                 'position' : 'relative',
10581                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10582             });
10583         }
10584         
10585         if(this.lazyLoad){
10586             
10587             var scrollHeight = this.bodyEl.dom.scrollHeight;
10588             
10589             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10590             
10591             var height = this.bodyEl.getHeight();
10592             
10593             if(scrollHeight - height == scrollTop) {
10594                 
10595                 var total = this.ds.getTotalCount();
10596                 
10597                 if(this.footer.cursor + this.footer.pageSize < total){
10598                     
10599                     this.footer.ds.load({
10600                         params : {
10601                             start : this.footer.cursor + this.footer.pageSize,
10602                             limit : this.footer.pageSize
10603                         },
10604                         add : true
10605                     });
10606                 }
10607             }
10608             
10609         }
10610     },
10611     onColumnSplitterMoved : function(i, diff)
10612     {
10613         this.userResized = true;
10614         
10615         var cm = this.colModel;
10616         
10617         var w = this.getHeaderIndex(i).getWidth() + diff;
10618         
10619         
10620         cm.setColumnWidth(i, w, true);
10621         this.initCSS();
10622         //var cid = cm.getColumnId(i); << not used in this version?
10623        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10624         
10625         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10626         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10627         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10628 */
10629         //this.updateSplitters();
10630         //this.layout(); << ??
10631         this.fireEvent("columnresize", i, w);
10632     },
10633     onHeaderChange : function()
10634     {
10635         var header = this.renderHeader();
10636         var table = this.el.select('table', true).first();
10637         
10638         this.headEl.remove();
10639         this.headEl = table.createChild(header, this.bodyEl, false);
10640         
10641         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10642             e.on('click', this.sort, this);
10643         }, this);
10644         
10645         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10646             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10647         }
10648         
10649     },
10650     
10651     onHiddenChange : function(colModel, colIndex, hidden)
10652     {
10653         /*
10654         this.cm.setHidden()
10655         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10656         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10657         
10658         this.CSS.updateRule(thSelector, "display", "");
10659         this.CSS.updateRule(tdSelector, "display", "");
10660         
10661         if(hidden){
10662             this.CSS.updateRule(thSelector, "display", "none");
10663             this.CSS.updateRule(tdSelector, "display", "none");
10664         }
10665         */
10666         // onload calls initCSS()
10667         this.onHeaderChange();
10668         this.onLoad();
10669     },
10670     
10671     setColumnWidth: function(col_index, width)
10672     {
10673         // width = "md-2 xs-2..."
10674         if(!this.colModel.config[col_index]) {
10675             return;
10676         }
10677         
10678         var w = width.split(" ");
10679         
10680         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10681         
10682         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10683         
10684         
10685         for(var j = 0; j < w.length; j++) {
10686             
10687             if(!w[j]) {
10688                 continue;
10689             }
10690             
10691             var size_cls = w[j].split("-");
10692             
10693             if(!Number.isInteger(size_cls[1] * 1)) {
10694                 continue;
10695             }
10696             
10697             if(!this.colModel.config[col_index][size_cls[0]]) {
10698                 continue;
10699             }
10700             
10701             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10702                 continue;
10703             }
10704             
10705             h_row[0].classList.replace(
10706                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10707                 "col-"+size_cls[0]+"-"+size_cls[1]
10708             );
10709             
10710             for(var i = 0; i < rows.length; i++) {
10711                 
10712                 var size_cls = w[j].split("-");
10713                 
10714                 if(!Number.isInteger(size_cls[1] * 1)) {
10715                     continue;
10716                 }
10717                 
10718                 if(!this.colModel.config[col_index][size_cls[0]]) {
10719                     continue;
10720                 }
10721                 
10722                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10723                     continue;
10724                 }
10725                 
10726                 rows[i].classList.replace(
10727                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10728                     "col-"+size_cls[0]+"-"+size_cls[1]
10729                 );
10730             }
10731             
10732             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10733         }
10734     }
10735 });
10736
10737 // currently only used to find the split on drag.. 
10738 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10739
10740 /**
10741  * @depricated
10742 */
10743 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10744 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10745 /*
10746  * - LGPL
10747  *
10748  * table cell
10749  * 
10750  */
10751
10752 /**
10753  * @class Roo.bootstrap.TableCell
10754  * @extends Roo.bootstrap.Component
10755  * @children Roo.bootstrap.Component
10756  * @parent Roo.bootstrap.TableRow
10757  * Bootstrap TableCell class
10758  * 
10759  * @cfg {String} html cell contain text
10760  * @cfg {String} cls cell class
10761  * @cfg {String} tag cell tag (td|th) default td
10762  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10763  * @cfg {String} align Aligns the content in a cell
10764  * @cfg {String} axis Categorizes cells
10765  * @cfg {String} bgcolor Specifies the background color of a cell
10766  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10767  * @cfg {Number} colspan Specifies the number of columns a cell should span
10768  * @cfg {String} headers Specifies one or more header cells a cell is related to
10769  * @cfg {Number} height Sets the height of a cell
10770  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10771  * @cfg {Number} rowspan Sets the number of rows a cell should span
10772  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10773  * @cfg {String} valign Vertical aligns the content in a cell
10774  * @cfg {Number} width Specifies the width of a cell
10775  * 
10776  * @constructor
10777  * Create a new TableCell
10778  * @param {Object} config The config object
10779  */
10780
10781 Roo.bootstrap.TableCell = function(config){
10782     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10783 };
10784
10785 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10786     
10787     html: false,
10788     cls: false,
10789     tag: false,
10790     abbr: false,
10791     align: false,
10792     axis: false,
10793     bgcolor: false,
10794     charoff: false,
10795     colspan: false,
10796     headers: false,
10797     height: false,
10798     nowrap: false,
10799     rowspan: false,
10800     scope: false,
10801     valign: false,
10802     width: false,
10803     
10804     
10805     getAutoCreate : function(){
10806         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10807         
10808         cfg = {
10809             tag: 'td'
10810         };
10811         
10812         if(this.tag){
10813             cfg.tag = this.tag;
10814         }
10815         
10816         if (this.html) {
10817             cfg.html=this.html
10818         }
10819         if (this.cls) {
10820             cfg.cls=this.cls
10821         }
10822         if (this.abbr) {
10823             cfg.abbr=this.abbr
10824         }
10825         if (this.align) {
10826             cfg.align=this.align
10827         }
10828         if (this.axis) {
10829             cfg.axis=this.axis
10830         }
10831         if (this.bgcolor) {
10832             cfg.bgcolor=this.bgcolor
10833         }
10834         if (this.charoff) {
10835             cfg.charoff=this.charoff
10836         }
10837         if (this.colspan) {
10838             cfg.colspan=this.colspan
10839         }
10840         if (this.headers) {
10841             cfg.headers=this.headers
10842         }
10843         if (this.height) {
10844             cfg.height=this.height
10845         }
10846         if (this.nowrap) {
10847             cfg.nowrap=this.nowrap
10848         }
10849         if (this.rowspan) {
10850             cfg.rowspan=this.rowspan
10851         }
10852         if (this.scope) {
10853             cfg.scope=this.scope
10854         }
10855         if (this.valign) {
10856             cfg.valign=this.valign
10857         }
10858         if (this.width) {
10859             cfg.width=this.width
10860         }
10861         
10862         
10863         return cfg;
10864     }
10865    
10866 });
10867
10868  
10869
10870  /*
10871  * - LGPL
10872  *
10873  * table row
10874  * 
10875  */
10876
10877 /**
10878  * @class Roo.bootstrap.TableRow
10879  * @extends Roo.bootstrap.Component
10880  * @children Roo.bootstrap.TableCell
10881  * @parent Roo.bootstrap.TableBody
10882  * Bootstrap TableRow class
10883  * @cfg {String} cls row class
10884  * @cfg {String} align Aligns the content in a table row
10885  * @cfg {String} bgcolor Specifies a background color for a table row
10886  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10887  * @cfg {String} valign Vertical aligns the content in a table row
10888  * 
10889  * @constructor
10890  * Create a new TableRow
10891  * @param {Object} config The config object
10892  */
10893
10894 Roo.bootstrap.TableRow = function(config){
10895     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10896 };
10897
10898 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10899     
10900     cls: false,
10901     align: false,
10902     bgcolor: false,
10903     charoff: false,
10904     valign: false,
10905     
10906     getAutoCreate : function(){
10907         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10908         
10909         cfg = {
10910             tag: 'tr'
10911         };
10912             
10913         if(this.cls){
10914             cfg.cls = this.cls;
10915         }
10916         if(this.align){
10917             cfg.align = this.align;
10918         }
10919         if(this.bgcolor){
10920             cfg.bgcolor = this.bgcolor;
10921         }
10922         if(this.charoff){
10923             cfg.charoff = this.charoff;
10924         }
10925         if(this.valign){
10926             cfg.valign = this.valign;
10927         }
10928         
10929         return cfg;
10930     }
10931    
10932 });
10933
10934  
10935
10936  /*
10937  * - LGPL
10938  *
10939  * table body
10940  * 
10941  */
10942
10943 /**
10944  * @class Roo.bootstrap.TableBody
10945  * @extends Roo.bootstrap.Component
10946  * @children Roo.bootstrap.TableRow
10947  * @parent Roo.bootstrap.Table
10948  * Bootstrap TableBody class
10949  * @cfg {String} cls element class
10950  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10951  * @cfg {String} align Aligns the content inside the element
10952  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10953  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10954  * 
10955  * @constructor
10956  * Create a new TableBody
10957  * @param {Object} config The config object
10958  */
10959
10960 Roo.bootstrap.TableBody = function(config){
10961     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10962 };
10963
10964 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10965     
10966     cls: false,
10967     tag: false,
10968     align: false,
10969     charoff: false,
10970     valign: false,
10971     
10972     getAutoCreate : function(){
10973         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10974         
10975         cfg = {
10976             tag: 'tbody'
10977         };
10978             
10979         if (this.cls) {
10980             cfg.cls=this.cls
10981         }
10982         if(this.tag){
10983             cfg.tag = this.tag;
10984         }
10985         
10986         if(this.align){
10987             cfg.align = this.align;
10988         }
10989         if(this.charoff){
10990             cfg.charoff = this.charoff;
10991         }
10992         if(this.valign){
10993             cfg.valign = this.valign;
10994         }
10995         
10996         return cfg;
10997     }
10998     
10999     
11000 //    initEvents : function()
11001 //    {
11002 //        
11003 //        if(!this.store){
11004 //            return;
11005 //        }
11006 //        
11007 //        this.store = Roo.factory(this.store, Roo.data);
11008 //        this.store.on('load', this.onLoad, this);
11009 //        
11010 //        this.store.load();
11011 //        
11012 //    },
11013 //    
11014 //    onLoad: function () 
11015 //    {   
11016 //        this.fireEvent('load', this);
11017 //    }
11018 //    
11019 //   
11020 });
11021
11022  
11023
11024  /*
11025  * Based on:
11026  * Ext JS Library 1.1.1
11027  * Copyright(c) 2006-2007, Ext JS, LLC.
11028  *
11029  * Originally Released Under LGPL - original licence link has changed is not relivant.
11030  *
11031  * Fork - LGPL
11032  * <script type="text/javascript">
11033  */
11034
11035 // as we use this in bootstrap.
11036 Roo.namespace('Roo.form');
11037  /**
11038  * @class Roo.form.Action
11039  * Internal Class used to handle form actions
11040  * @constructor
11041  * @param {Roo.form.BasicForm} el The form element or its id
11042  * @param {Object} config Configuration options
11043  */
11044
11045  
11046  
11047 // define the action interface
11048 Roo.form.Action = function(form, options){
11049     this.form = form;
11050     this.options = options || {};
11051 };
11052 /**
11053  * Client Validation Failed
11054  * @const 
11055  */
11056 Roo.form.Action.CLIENT_INVALID = 'client';
11057 /**
11058  * Server Validation Failed
11059  * @const 
11060  */
11061 Roo.form.Action.SERVER_INVALID = 'server';
11062  /**
11063  * Connect to Server Failed
11064  * @const 
11065  */
11066 Roo.form.Action.CONNECT_FAILURE = 'connect';
11067 /**
11068  * Reading Data from Server Failed
11069  * @const 
11070  */
11071 Roo.form.Action.LOAD_FAILURE = 'load';
11072
11073 Roo.form.Action.prototype = {
11074     type : 'default',
11075     failureType : undefined,
11076     response : undefined,
11077     result : undefined,
11078
11079     // interface method
11080     run : function(options){
11081
11082     },
11083
11084     // interface method
11085     success : function(response){
11086
11087     },
11088
11089     // interface method
11090     handleResponse : function(response){
11091
11092     },
11093
11094     // default connection failure
11095     failure : function(response){
11096         
11097         this.response = response;
11098         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11099         this.form.afterAction(this, false);
11100     },
11101
11102     processResponse : function(response){
11103         this.response = response;
11104         if(!response.responseText){
11105             return true;
11106         }
11107         this.result = this.handleResponse(response);
11108         return this.result;
11109     },
11110
11111     // utility functions used internally
11112     getUrl : function(appendParams){
11113         var url = this.options.url || this.form.url || this.form.el.dom.action;
11114         if(appendParams){
11115             var p = this.getParams();
11116             if(p){
11117                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11118             }
11119         }
11120         return url;
11121     },
11122
11123     getMethod : function(){
11124         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11125     },
11126
11127     getParams : function(){
11128         var bp = this.form.baseParams;
11129         var p = this.options.params;
11130         if(p){
11131             if(typeof p == "object"){
11132                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11133             }else if(typeof p == 'string' && bp){
11134                 p += '&' + Roo.urlEncode(bp);
11135             }
11136         }else if(bp){
11137             p = Roo.urlEncode(bp);
11138         }
11139         return p;
11140     },
11141
11142     createCallback : function(){
11143         return {
11144             success: this.success,
11145             failure: this.failure,
11146             scope: this,
11147             timeout: (this.form.timeout*1000),
11148             upload: this.form.fileUpload ? this.success : undefined
11149         };
11150     }
11151 };
11152
11153 Roo.form.Action.Submit = function(form, options){
11154     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11155 };
11156
11157 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11158     type : 'submit',
11159
11160     haveProgress : false,
11161     uploadComplete : false,
11162     
11163     // uploadProgress indicator.
11164     uploadProgress : function()
11165     {
11166         if (!this.form.progressUrl) {
11167             return;
11168         }
11169         
11170         if (!this.haveProgress) {
11171             Roo.MessageBox.progress("Uploading", "Uploading");
11172         }
11173         if (this.uploadComplete) {
11174            Roo.MessageBox.hide();
11175            return;
11176         }
11177         
11178         this.haveProgress = true;
11179    
11180         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11181         
11182         var c = new Roo.data.Connection();
11183         c.request({
11184             url : this.form.progressUrl,
11185             params: {
11186                 id : uid
11187             },
11188             method: 'GET',
11189             success : function(req){
11190                //console.log(data);
11191                 var rdata = false;
11192                 var edata;
11193                 try  {
11194                    rdata = Roo.decode(req.responseText)
11195                 } catch (e) {
11196                     Roo.log("Invalid data from server..");
11197                     Roo.log(edata);
11198                     return;
11199                 }
11200                 if (!rdata || !rdata.success) {
11201                     Roo.log(rdata);
11202                     Roo.MessageBox.alert(Roo.encode(rdata));
11203                     return;
11204                 }
11205                 var data = rdata.data;
11206                 
11207                 if (this.uploadComplete) {
11208                    Roo.MessageBox.hide();
11209                    return;
11210                 }
11211                    
11212                 if (data){
11213                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11214                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11215                     );
11216                 }
11217                 this.uploadProgress.defer(2000,this);
11218             },
11219        
11220             failure: function(data) {
11221                 Roo.log('progress url failed ');
11222                 Roo.log(data);
11223             },
11224             scope : this
11225         });
11226            
11227     },
11228     
11229     
11230     run : function()
11231     {
11232         // run get Values on the form, so it syncs any secondary forms.
11233         this.form.getValues();
11234         
11235         var o = this.options;
11236         var method = this.getMethod();
11237         var isPost = method == 'POST';
11238         if(o.clientValidation === false || this.form.isValid()){
11239             
11240             if (this.form.progressUrl) {
11241                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11242                     (new Date() * 1) + '' + Math.random());
11243                     
11244             } 
11245             
11246             
11247             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11248                 form:this.form.el.dom,
11249                 url:this.getUrl(!isPost),
11250                 method: method,
11251                 params:isPost ? this.getParams() : null,
11252                 isUpload: this.form.fileUpload,
11253                 formData : this.form.formData
11254             }));
11255             
11256             this.uploadProgress();
11257
11258         }else if (o.clientValidation !== false){ // client validation failed
11259             this.failureType = Roo.form.Action.CLIENT_INVALID;
11260             this.form.afterAction(this, false);
11261         }
11262     },
11263
11264     success : function(response)
11265     {
11266         this.uploadComplete= true;
11267         if (this.haveProgress) {
11268             Roo.MessageBox.hide();
11269         }
11270         
11271         
11272         var result = this.processResponse(response);
11273         if(result === true || result.success){
11274             this.form.afterAction(this, true);
11275             return;
11276         }
11277         if(result.errors){
11278             this.form.markInvalid(result.errors);
11279             this.failureType = Roo.form.Action.SERVER_INVALID;
11280         }
11281         this.form.afterAction(this, false);
11282     },
11283     failure : function(response)
11284     {
11285         this.uploadComplete= true;
11286         if (this.haveProgress) {
11287             Roo.MessageBox.hide();
11288         }
11289         
11290         this.response = response;
11291         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11292         this.form.afterAction(this, false);
11293     },
11294     
11295     handleResponse : function(response){
11296         if(this.form.errorReader){
11297             var rs = this.form.errorReader.read(response);
11298             var errors = [];
11299             if(rs.records){
11300                 for(var i = 0, len = rs.records.length; i < len; i++) {
11301                     var r = rs.records[i];
11302                     errors[i] = r.data;
11303                 }
11304             }
11305             if(errors.length < 1){
11306                 errors = null;
11307             }
11308             return {
11309                 success : rs.success,
11310                 errors : errors
11311             };
11312         }
11313         var ret = false;
11314         try {
11315             var rt = response.responseText;
11316             if (rt.match(/^\<!--\[CDATA\[/)) {
11317                 rt = rt.replace(/^\<!--\[CDATA\[/,'');
11318                 rt = rt.replace(/\]\]--\>$/,'');
11319             }
11320             
11321             ret = Roo.decode(rt);
11322         } catch (e) {
11323             ret = {
11324                 success: false,
11325                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11326                 errors : []
11327             };
11328         }
11329         return ret;
11330         
11331     }
11332 });
11333
11334
11335 Roo.form.Action.Load = function(form, options){
11336     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11337     this.reader = this.form.reader;
11338 };
11339
11340 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11341     type : 'load',
11342
11343     run : function(){
11344         
11345         Roo.Ajax.request(Roo.apply(
11346                 this.createCallback(), {
11347                     method:this.getMethod(),
11348                     url:this.getUrl(false),
11349                     params:this.getParams()
11350         }));
11351     },
11352
11353     success : function(response){
11354         
11355         var result = this.processResponse(response);
11356         if(result === true || !result.success || !result.data){
11357             this.failureType = Roo.form.Action.LOAD_FAILURE;
11358             this.form.afterAction(this, false);
11359             return;
11360         }
11361         this.form.clearInvalid();
11362         this.form.setValues(result.data);
11363         this.form.afterAction(this, true);
11364     },
11365
11366     handleResponse : function(response){
11367         if(this.form.reader){
11368             var rs = this.form.reader.read(response);
11369             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11370             return {
11371                 success : rs.success,
11372                 data : data
11373             };
11374         }
11375         return Roo.decode(response.responseText);
11376     }
11377 });
11378
11379 Roo.form.Action.ACTION_TYPES = {
11380     'load' : Roo.form.Action.Load,
11381     'submit' : Roo.form.Action.Submit
11382 };/*
11383  * - LGPL
11384  *
11385  * form
11386  *
11387  */
11388
11389 /**
11390  * @class Roo.bootstrap.form.Form
11391  * @extends Roo.bootstrap.Component
11392  * @children Roo.bootstrap.Component
11393  * Bootstrap Form class
11394  * @cfg {String} method  GET | POST (default POST)
11395  * @cfg {String} labelAlign top | left (default top)
11396  * @cfg {String} align left  | right - for navbars
11397  * @cfg {Boolean} loadMask load mask when submit (default true)
11398
11399  *
11400  * @constructor
11401  * Create a new Form
11402  * @param {Object} config The config object
11403  */
11404
11405
11406 Roo.bootstrap.form.Form = function(config){
11407     
11408     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11409     
11410     Roo.bootstrap.form.Form.popover.apply();
11411     
11412     this.addEvents({
11413         /**
11414          * @event clientvalidation
11415          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11416          * @param {Form} this
11417          * @param {Boolean} valid true if the form has passed client-side validation
11418          */
11419         clientvalidation: true,
11420         /**
11421          * @event beforeaction
11422          * Fires before any action is performed. Return false to cancel the action.
11423          * @param {Form} this
11424          * @param {Action} action The action to be performed
11425          */
11426         beforeaction: true,
11427         /**
11428          * @event actionfailed
11429          * Fires when an action fails.
11430          * @param {Form} this
11431          * @param {Action} action The action that failed
11432          */
11433         actionfailed : true,
11434         /**
11435          * @event actioncomplete
11436          * Fires when an action is completed.
11437          * @param {Form} this
11438          * @param {Action} action The action that completed
11439          */
11440         actioncomplete : true
11441     });
11442 };
11443
11444 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11445
11446      /**
11447      * @cfg {String} method
11448      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11449      */
11450     method : 'POST',
11451     /**
11452      * @cfg {String} url
11453      * The URL to use for form actions if one isn't supplied in the action options.
11454      */
11455     /**
11456      * @cfg {Boolean} fileUpload
11457      * Set to true if this form is a file upload.
11458      */
11459
11460     /**
11461      * @cfg {Object} baseParams
11462      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11463      */
11464
11465     /**
11466      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11467      */
11468     timeout: 30,
11469     /**
11470      * @cfg {Sting} align (left|right) for navbar forms
11471      */
11472     align : 'left',
11473
11474     // private
11475     activeAction : null,
11476
11477     /**
11478      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11479      * element by passing it or its id or mask the form itself by passing in true.
11480      * @type Mixed
11481      */
11482     waitMsgTarget : false,
11483
11484     loadMask : true,
11485     
11486     /**
11487      * @cfg {Boolean} errorMask (true|false) default false
11488      */
11489     errorMask : false,
11490     
11491     /**
11492      * @cfg {Number} maskOffset Default 100
11493      */
11494     maskOffset : 100,
11495     
11496     /**
11497      * @cfg {Boolean} maskBody
11498      */
11499     maskBody : false,
11500
11501     getAutoCreate : function(){
11502
11503         var cfg = {
11504             tag: 'form',
11505             method : this.method || 'POST',
11506             id : this.id || Roo.id(),
11507             cls : ''
11508         };
11509         if (this.parent().xtype.match(/^Nav/)) {
11510             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11511
11512         }
11513
11514         if (this.labelAlign == 'left' ) {
11515             cfg.cls += ' form-horizontal';
11516         }
11517
11518
11519         return cfg;
11520     },
11521     initEvents : function()
11522     {
11523         this.el.on('submit', this.onSubmit, this);
11524         // this was added as random key presses on the form where triggering form submit.
11525         this.el.on('keypress', function(e) {
11526             if (e.getCharCode() != 13) {
11527                 return true;
11528             }
11529             // we might need to allow it for textareas.. and some other items.
11530             // check e.getTarget().
11531
11532             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11533                 return true;
11534             }
11535
11536             Roo.log("keypress blocked");
11537
11538             e.preventDefault();
11539             return false;
11540         });
11541         
11542     },
11543     // private
11544     onSubmit : function(e){
11545         e.stopEvent();
11546     },
11547
11548      /**
11549      * Returns true if client-side validation on the form is successful.
11550      * @return Boolean
11551      */
11552     isValid : function(){
11553         var items = this.getItems();
11554         var valid = true;
11555         var target = false;
11556         
11557         items.each(function(f){
11558             
11559             if(f.validate()){
11560                 return;
11561             }
11562             
11563             Roo.log('invalid field: ' + f.name);
11564             
11565             valid = false;
11566
11567             if(!target && f.el.isVisible(true)){
11568                 target = f;
11569             }
11570            
11571         });
11572         
11573         if(this.errorMask && !valid){
11574             Roo.bootstrap.form.Form.popover.mask(this, target);
11575         }
11576         
11577         return valid;
11578     },
11579     
11580     /**
11581      * Returns true if any fields in this form have changed since their original load.
11582      * @return Boolean
11583      */
11584     isDirty : function(){
11585         var dirty = false;
11586         var items = this.getItems();
11587         items.each(function(f){
11588            if(f.isDirty()){
11589                dirty = true;
11590                return false;
11591            }
11592            return true;
11593         });
11594         return dirty;
11595     },
11596      /**
11597      * Performs a predefined action (submit or load) or custom actions you define on this form.
11598      * @param {String} actionName The name of the action type
11599      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11600      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11601      * accept other config options):
11602      * <pre>
11603 Property          Type             Description
11604 ----------------  ---------------  ----------------------------------------------------------------------------------
11605 url               String           The url for the action (defaults to the form's url)
11606 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11607 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11608 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11609                                    validate the form on the client (defaults to false)
11610      * </pre>
11611      * @return {BasicForm} this
11612      */
11613     doAction : function(action, options){
11614         if(typeof action == 'string'){
11615             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11616         }
11617         if(this.fireEvent('beforeaction', this, action) !== false){
11618             this.beforeAction(action);
11619             action.run.defer(100, action);
11620         }
11621         return this;
11622     },
11623
11624     // private
11625     beforeAction : function(action){
11626         var o = action.options;
11627         
11628         if(this.loadMask){
11629             
11630             if(this.maskBody){
11631                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11632             } else {
11633                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11634             }
11635         }
11636         // not really supported yet.. ??
11637
11638         //if(this.waitMsgTarget === true){
11639         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11640         //}else if(this.waitMsgTarget){
11641         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11642         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11643         //}else {
11644         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11645        // }
11646
11647     },
11648
11649     // private
11650     afterAction : function(action, success){
11651         this.activeAction = null;
11652         var o = action.options;
11653
11654         if(this.loadMask){
11655             
11656             if(this.maskBody){
11657                 Roo.get(document.body).unmask();
11658             } else {
11659                 this.el.unmask();
11660             }
11661         }
11662         
11663         //if(this.waitMsgTarget === true){
11664 //            this.el.unmask();
11665         //}else if(this.waitMsgTarget){
11666         //    this.waitMsgTarget.unmask();
11667         //}else{
11668         //    Roo.MessageBox.updateProgress(1);
11669         //    Roo.MessageBox.hide();
11670        // }
11671         //
11672         if(success){
11673             if(o.reset){
11674                 this.reset();
11675             }
11676             Roo.callback(o.success, o.scope, [this, action]);
11677             this.fireEvent('actioncomplete', this, action);
11678
11679         }else{
11680
11681             // failure condition..
11682             // we have a scenario where updates need confirming.
11683             // eg. if a locking scenario exists..
11684             // we look for { errors : { needs_confirm : true }} in the response.
11685             if (
11686                 (typeof(action.result) != 'undefined')  &&
11687                 (typeof(action.result.errors) != 'undefined')  &&
11688                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11689            ){
11690                 var _t = this;
11691                 Roo.log("not supported yet");
11692                  /*
11693
11694                 Roo.MessageBox.confirm(
11695                     "Change requires confirmation",
11696                     action.result.errorMsg,
11697                     function(r) {
11698                         if (r != 'yes') {
11699                             return;
11700                         }
11701                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11702                     }
11703
11704                 );
11705                 */
11706
11707
11708                 return;
11709             }
11710
11711             Roo.callback(o.failure, o.scope, [this, action]);
11712             // show an error message if no failed handler is set..
11713             if (!this.hasListener('actionfailed')) {
11714                 Roo.log("need to add dialog support");
11715                 /*
11716                 Roo.MessageBox.alert("Error",
11717                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11718                         action.result.errorMsg :
11719                         "Saving Failed, please check your entries or try again"
11720                 );
11721                 */
11722             }
11723
11724             this.fireEvent('actionfailed', this, action);
11725         }
11726
11727     },
11728     /**
11729      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11730      * @param {String} id The value to search for
11731      * @return Field
11732      */
11733     findField : function(id){
11734         var items = this.getItems();
11735         var field = items.get(id);
11736         if(!field){
11737              items.each(function(f){
11738                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11739                     field = f;
11740                     return false;
11741                 }
11742                 return true;
11743             });
11744         }
11745         return field || null;
11746     },
11747      /**
11748      * Mark fields in this form invalid in bulk.
11749      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11750      * @return {BasicForm} this
11751      */
11752     markInvalid : function(errors){
11753         if(errors instanceof Array){
11754             for(var i = 0, len = errors.length; i < len; i++){
11755                 var fieldError = errors[i];
11756                 var f = this.findField(fieldError.id);
11757                 if(f){
11758                     f.markInvalid(fieldError.msg);
11759                 }
11760             }
11761         }else{
11762             var field, id;
11763             for(id in errors){
11764                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11765                     field.markInvalid(errors[id]);
11766                 }
11767             }
11768         }
11769         //Roo.each(this.childForms || [], function (f) {
11770         //    f.markInvalid(errors);
11771         //});
11772
11773         return this;
11774     },
11775
11776     /**
11777      * Set values for fields in this form in bulk.
11778      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11779      * @return {BasicForm} this
11780      */
11781     setValues : function(values){
11782         if(values instanceof Array){ // array of objects
11783             for(var i = 0, len = values.length; i < len; i++){
11784                 var v = values[i];
11785                 var f = this.findField(v.id);
11786                 if(f){
11787                     f.setValue(v.value);
11788                     if(this.trackResetOnLoad){
11789                         f.originalValue = f.getValue();
11790                     }
11791                 }
11792             }
11793         }else{ // object hash
11794             var field, id;
11795             for(id in values){
11796                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11797
11798                     if (field.setFromData &&
11799                         field.valueField &&
11800                         field.displayField &&
11801                         // combos' with local stores can
11802                         // be queried via setValue()
11803                         // to set their value..
11804                         (field.store && !field.store.isLocal)
11805                         ) {
11806                         // it's a combo
11807                         var sd = { };
11808                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11809                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11810                         field.setFromData(sd);
11811
11812                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11813                         
11814                         field.setFromData(values);
11815                         
11816                     } else {
11817                         field.setValue(values[id]);
11818                     }
11819
11820
11821                     if(this.trackResetOnLoad){
11822                         field.originalValue = field.getValue();
11823                     }
11824                 }
11825             }
11826         }
11827
11828         //Roo.each(this.childForms || [], function (f) {
11829         //    f.setValues(values);
11830         //});
11831
11832         return this;
11833     },
11834
11835     /**
11836      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11837      * they are returned as an array.
11838      * @param {Boolean} asString
11839      * @return {Object}
11840      */
11841     getValues : function(asString){
11842         //if (this.childForms) {
11843             // copy values from the child forms
11844         //    Roo.each(this.childForms, function (f) {
11845         //        this.setValues(f.getValues());
11846         //    }, this);
11847         //}
11848
11849
11850
11851         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11852         if(asString === true){
11853             return fs;
11854         }
11855         return Roo.urlDecode(fs);
11856     },
11857
11858     /**
11859      * Returns the fields in this form as an object with key/value pairs.
11860      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11861      * @return {Object}
11862      */
11863     getFieldValues : function(with_hidden)
11864     {
11865         var items = this.getItems();
11866         var ret = {};
11867         items.each(function(f){
11868             
11869             if (!f.getName()) {
11870                 return;
11871             }
11872             
11873             var v = f.getValue();
11874             
11875             if (f.inputType =='radio') {
11876                 if (typeof(ret[f.getName()]) == 'undefined') {
11877                     ret[f.getName()] = ''; // empty..
11878                 }
11879
11880                 if (!f.el.dom.checked) {
11881                     return;
11882
11883                 }
11884                 v = f.el.dom.value;
11885
11886             }
11887             
11888             if(f.xtype == 'MoneyField'){
11889                 ret[f.currencyName] = f.getCurrency();
11890             }
11891
11892             // not sure if this supported any more..
11893             if ((typeof(v) == 'object') && f.getRawValue) {
11894                 v = f.getRawValue() ; // dates..
11895             }
11896             // combo boxes where name != hiddenName...
11897             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11898                 ret[f.name] = f.getRawValue();
11899             }
11900             ret[f.getName()] = v;
11901         });
11902
11903         return ret;
11904     },
11905
11906     /**
11907      * Clears all invalid messages in this form.
11908      * @return {BasicForm} this
11909      */
11910     clearInvalid : function(){
11911         var items = this.getItems();
11912
11913         items.each(function(f){
11914            f.clearInvalid();
11915         });
11916
11917         return this;
11918     },
11919
11920     /**
11921      * Resets this form.
11922      * @return {BasicForm} this
11923      */
11924     reset : function(){
11925         var items = this.getItems();
11926         items.each(function(f){
11927             f.reset();
11928         });
11929
11930         Roo.each(this.childForms || [], function (f) {
11931             f.reset();
11932         });
11933
11934
11935         return this;
11936     },
11937     
11938     getItems : function()
11939     {
11940         var r=new Roo.util.MixedCollection(false, function(o){
11941             return o.id || (o.id = Roo.id());
11942         });
11943         var iter = function(el) {
11944             if (el.inputEl) {
11945                 r.add(el);
11946             }
11947             if (!el.items) {
11948                 return;
11949             }
11950             Roo.each(el.items,function(e) {
11951                 iter(e);
11952             });
11953         };
11954
11955         iter(this);
11956         return r;
11957     },
11958     
11959     hideFields : function(items)
11960     {
11961         Roo.each(items, function(i){
11962             
11963             var f = this.findField(i);
11964             
11965             if(!f){
11966                 return;
11967             }
11968             
11969             f.hide();
11970             
11971         }, this);
11972     },
11973     
11974     showFields : function(items)
11975     {
11976         Roo.each(items, function(i){
11977             
11978             var f = this.findField(i);
11979             
11980             if(!f){
11981                 return;
11982             }
11983             
11984             f.show();
11985             
11986         }, this);
11987     }
11988
11989 });
11990
11991 Roo.apply(Roo.bootstrap.form.Form, {
11992     
11993     popover : {
11994         
11995         padding : 5,
11996         
11997         isApplied : false,
11998         
11999         isMasked : false,
12000         
12001         form : false,
12002         
12003         target : false,
12004         
12005         toolTip : false,
12006         
12007         intervalID : false,
12008         
12009         maskEl : false,
12010         
12011         apply : function()
12012         {
12013             if(this.isApplied){
12014                 return;
12015             }
12016             
12017             this.maskEl = {
12018                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
12019                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
12020                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
12021                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
12022             };
12023             
12024             this.maskEl.top.enableDisplayMode("block");
12025             this.maskEl.left.enableDisplayMode("block");
12026             this.maskEl.bottom.enableDisplayMode("block");
12027             this.maskEl.right.enableDisplayMode("block");
12028             
12029             this.toolTip = new Roo.bootstrap.Tooltip({
12030                 cls : 'roo-form-error-popover',
12031                 alignment : {
12032                     'left' : ['r-l', [-2,0], 'right'],
12033                     'right' : ['l-r', [2,0], 'left'],
12034                     'bottom' : ['tl-bl', [0,2], 'top'],
12035                     'top' : [ 'bl-tl', [0,-2], 'bottom']
12036                 }
12037             });
12038             
12039             this.toolTip.render(Roo.get(document.body));
12040
12041             this.toolTip.el.enableDisplayMode("block");
12042             
12043             Roo.get(document.body).on('click', function(){
12044                 this.unmask();
12045             }, this);
12046             
12047             Roo.get(document.body).on('touchstart', function(){
12048                 this.unmask();
12049             }, this);
12050             
12051             this.isApplied = true
12052         },
12053         
12054         mask : function(form, target)
12055         {
12056             this.form = form;
12057             
12058             this.target = target;
12059             
12060             if(!this.form.errorMask || !target.el){
12061                 return;
12062             }
12063             
12064             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12065             
12066             Roo.log(scrollable);
12067             
12068             var ot = this.target.el.calcOffsetsTo(scrollable);
12069             
12070             var scrollTo = ot[1] - this.form.maskOffset;
12071             
12072             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12073             
12074             scrollable.scrollTo('top', scrollTo);
12075             
12076             var box = this.target.el.getBox();
12077             Roo.log(box);
12078             var zIndex = Roo.bootstrap.Modal.zIndex++;
12079
12080             
12081             this.maskEl.top.setStyle('position', 'absolute');
12082             this.maskEl.top.setStyle('z-index', zIndex);
12083             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12084             this.maskEl.top.setLeft(0);
12085             this.maskEl.top.setTop(0);
12086             this.maskEl.top.show();
12087             
12088             this.maskEl.left.setStyle('position', 'absolute');
12089             this.maskEl.left.setStyle('z-index', zIndex);
12090             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12091             this.maskEl.left.setLeft(0);
12092             this.maskEl.left.setTop(box.y - this.padding);
12093             this.maskEl.left.show();
12094
12095             this.maskEl.bottom.setStyle('position', 'absolute');
12096             this.maskEl.bottom.setStyle('z-index', zIndex);
12097             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12098             this.maskEl.bottom.setLeft(0);
12099             this.maskEl.bottom.setTop(box.bottom + this.padding);
12100             this.maskEl.bottom.show();
12101
12102             this.maskEl.right.setStyle('position', 'absolute');
12103             this.maskEl.right.setStyle('z-index', zIndex);
12104             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12105             this.maskEl.right.setLeft(box.right + this.padding);
12106             this.maskEl.right.setTop(box.y - this.padding);
12107             this.maskEl.right.show();
12108
12109             this.toolTip.bindEl = this.target.el;
12110
12111             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12112
12113             var tip = this.target.blankText;
12114
12115             if(this.target.getValue() !== '' ) {
12116                 
12117                 if (this.target.invalidText.length) {
12118                     tip = this.target.invalidText;
12119                 } else if (this.target.regexText.length){
12120                     tip = this.target.regexText;
12121                 }
12122             }
12123
12124             this.toolTip.show(tip);
12125
12126             this.intervalID = window.setInterval(function() {
12127                 Roo.bootstrap.form.Form.popover.unmask();
12128             }, 10000);
12129
12130             window.onwheel = function(){ return false;};
12131             
12132             (function(){ this.isMasked = true; }).defer(500, this);
12133             
12134         },
12135         
12136         unmask : function()
12137         {
12138             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12139                 return;
12140             }
12141             
12142             this.maskEl.top.setStyle('position', 'absolute');
12143             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12144             this.maskEl.top.hide();
12145
12146             this.maskEl.left.setStyle('position', 'absolute');
12147             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12148             this.maskEl.left.hide();
12149
12150             this.maskEl.bottom.setStyle('position', 'absolute');
12151             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12152             this.maskEl.bottom.hide();
12153
12154             this.maskEl.right.setStyle('position', 'absolute');
12155             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12156             this.maskEl.right.hide();
12157             
12158             this.toolTip.hide();
12159             
12160             this.toolTip.el.hide();
12161             
12162             window.onwheel = function(){ return true;};
12163             
12164             if(this.intervalID){
12165                 window.clearInterval(this.intervalID);
12166                 this.intervalID = false;
12167             }
12168             
12169             this.isMasked = false;
12170             
12171         }
12172         
12173     }
12174     
12175 });
12176
12177 /*
12178  * Based on:
12179  * Ext JS Library 1.1.1
12180  * Copyright(c) 2006-2007, Ext JS, LLC.
12181  *
12182  * Originally Released Under LGPL - original licence link has changed is not relivant.
12183  *
12184  * Fork - LGPL
12185  * <script type="text/javascript">
12186  */
12187 /**
12188  * @class Roo.form.VTypes
12189  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12190  * @static
12191  */
12192 Roo.form.VTypes = function(){
12193     // closure these in so they are only created once.
12194     var alpha = /^[a-zA-Z_]+$/;
12195     var alphanum = /^[a-zA-Z0-9_]+$/;
12196     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12197     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12198
12199     // All these messages and functions are configurable
12200     return {
12201         /**
12202          * The function used to validate email addresses
12203          * @param {String} value The email address
12204          */
12205         email : function(v){
12206             return email.test(v);
12207         },
12208         /**
12209          * The error text to display when the email validation function returns false
12210          * @type String
12211          */
12212         emailText : 'This field should be an e-mail address in the format "user@domain.com"',
12213         /**
12214          * The keystroke filter mask to be applied on email input
12215          * @type RegExp
12216          */
12217         emailMask : /[a-z0-9_\.\-@]/i,
12218
12219         /**
12220          * The function used to validate URLs
12221          * @param {String} value The URL
12222          */
12223         url : function(v){
12224             return url.test(v);
12225         },
12226         /**
12227          * The error text to display when the url validation function returns false
12228          * @type String
12229          */
12230         urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12231         
12232         /**
12233          * The function used to validate alpha values
12234          * @param {String} value The value
12235          */
12236         alpha : function(v){
12237             return alpha.test(v);
12238         },
12239         /**
12240          * The error text to display when the alpha validation function returns false
12241          * @type String
12242          */
12243         alphaText : 'This field should only contain letters and _',
12244         /**
12245          * The keystroke filter mask to be applied on alpha input
12246          * @type RegExp
12247          */
12248         alphaMask : /[a-z_]/i,
12249
12250         /**
12251          * The function used to validate alphanumeric values
12252          * @param {String} value The value
12253          */
12254         alphanum : function(v){
12255             return alphanum.test(v);
12256         },
12257         /**
12258          * The error text to display when the alphanumeric validation function returns false
12259          * @type String
12260          */
12261         alphanumText : 'This field should only contain letters, numbers and _',
12262         /**
12263          * The keystroke filter mask to be applied on alphanumeric input
12264          * @type RegExp
12265          */
12266         alphanumMask : /[a-z0-9_]/i
12267     };
12268 }();/*
12269  * - LGPL
12270  *
12271  * Input
12272  * 
12273  */
12274
12275 /**
12276  * @class Roo.bootstrap.form.Input
12277  * @extends Roo.bootstrap.Component
12278  * Bootstrap Input class
12279  * @cfg {Boolean} disabled is it disabled
12280  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12281  * @cfg {String} name name of the input
12282  * @cfg {string} fieldLabel - the label associated
12283  * @cfg {string} placeholder - placeholder to put in text.
12284  * @cfg {string} before - input group add on before
12285  * @cfg {string} after - input group add on after
12286  * @cfg {string} size - (lg|sm) or leave empty..
12287  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12288  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12289  * @cfg {Number} md colspan out of 12 for computer-sized screens
12290  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12291  * @cfg {string} value default value of the input
12292  * @cfg {Number} labelWidth set the width of label 
12293  * @cfg {Number} labellg set the width of label (1-12)
12294  * @cfg {Number} labelmd set the width of label (1-12)
12295  * @cfg {Number} labelsm set the width of label (1-12)
12296  * @cfg {Number} labelxs set the width of label (1-12)
12297  * @cfg {String} labelAlign (top|left)
12298  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12299  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12300  * @cfg {String} indicatorpos (left|right) default left
12301  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12302  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12303  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12304  * @cfg {Roo.bootstrap.Button} before Button to show before
12305  * @cfg {Roo.bootstrap.Button} afterButton to show before
12306  * @cfg {String} align (left|center|right) Default left
12307  * @cfg {Boolean} forceFeedback (true|false) Default false
12308  * 
12309  * @constructor
12310  * Create a new Input
12311  * @param {Object} config The config object
12312  */
12313
12314 Roo.bootstrap.form.Input = function(config){
12315     
12316     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12317     
12318     this.addEvents({
12319         /**
12320          * @event focus
12321          * Fires when this field receives input focus.
12322          * @param {Roo.form.Field} this
12323          */
12324         focus : true,
12325         /**
12326          * @event blur
12327          * Fires when this field loses input focus.
12328          * @param {Roo.form.Field} this
12329          */
12330         blur : true,
12331         /**
12332          * @event specialkey
12333          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12334          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12335          * @param {Roo.form.Field} this
12336          * @param {Roo.EventObject} e The event object
12337          */
12338         specialkey : true,
12339         /**
12340          * @event change
12341          * Fires just before the field blurs if the field value has changed.
12342          * @param {Roo.form.Field} this
12343          * @param {Mixed} newValue The new value
12344          * @param {Mixed} oldValue The original value
12345          */
12346         change : true,
12347         /**
12348          * @event invalid
12349          * Fires after the field has been marked as invalid.
12350          * @param {Roo.form.Field} this
12351          * @param {String} msg The validation message
12352          */
12353         invalid : true,
12354         /**
12355          * @event valid
12356          * Fires after the field has been validated with no errors.
12357          * @param {Roo.form.Field} this
12358          */
12359         valid : true,
12360          /**
12361          * @event keyup
12362          * Fires after the key up
12363          * @param {Roo.form.Field} this
12364          * @param {Roo.EventObject}  e The event Object
12365          */
12366         keyup : true,
12367         /**
12368          * @event paste
12369          * Fires after the user pastes into input
12370          * @param {Roo.form.Field} this
12371          * @param {Roo.EventObject}  e The event Object
12372          */
12373         paste : true
12374     });
12375 };
12376
12377 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12378      /**
12379      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12380       automatic validation (defaults to "keyup").
12381      */
12382     validationEvent : "keyup",
12383      /**
12384      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12385      */
12386     validateOnBlur : true,
12387     /**
12388      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12389      */
12390     validationDelay : 250,
12391      /**
12392      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12393      */
12394     focusClass : "x-form-focus",  // not needed???
12395     
12396        
12397     /**
12398      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12399      */
12400     invalidClass : "has-warning",
12401     
12402     /**
12403      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12404      */
12405     validClass : "has-success",
12406     
12407     /**
12408      * @cfg {Boolean} hasFeedback (true|false) default true
12409      */
12410     hasFeedback : true,
12411     
12412     /**
12413      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12414      */
12415     invalidFeedbackClass : "glyphicon-warning-sign",
12416     
12417     /**
12418      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12419      */
12420     validFeedbackClass : "glyphicon-ok",
12421     
12422     /**
12423      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12424      */
12425     selectOnFocus : false,
12426     
12427      /**
12428      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12429      */
12430     maskRe : null,
12431        /**
12432      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12433      */
12434     vtype : null,
12435     
12436       /**
12437      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12438      */
12439     disableKeyFilter : false,
12440     
12441        /**
12442      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12443      */
12444     disabled : false,
12445      /**
12446      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12447      */
12448     allowBlank : true,
12449     /**
12450      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12451      */
12452     blankText : "Please complete this mandatory field",
12453     
12454      /**
12455      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12456      */
12457     minLength : 0,
12458     /**
12459      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12460      */
12461     maxLength : Number.MAX_VALUE,
12462     /**
12463      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12464      */
12465     minLengthText : "The minimum length for this field is {0}",
12466     /**
12467      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12468      */
12469     maxLengthText : "The maximum length for this field is {0}",
12470   
12471     
12472     /**
12473      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12474      * If available, this function will be called only after the basic validators all return true, and will be passed the
12475      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12476      */
12477     validator : null,
12478     /**
12479      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12480      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12481      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12482      */
12483     regex : null,
12484     /**
12485      * @cfg {String} regexText -- Depricated - use Invalid Text
12486      */
12487     regexText : "",
12488     
12489     /**
12490      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12491      */
12492     invalidText : "",
12493     
12494     
12495     
12496     autocomplete: false,
12497     
12498     
12499     fieldLabel : '',
12500     inputType : 'text',
12501     
12502     name : false,
12503     placeholder: false,
12504     before : false,
12505     after : false,
12506     size : false,
12507     hasFocus : false,
12508     preventMark: false,
12509     isFormField : true,
12510     value : '',
12511     labelWidth : 2,
12512     labelAlign : false,
12513     readOnly : false,
12514     align : false,
12515     formatedValue : false,
12516     forceFeedback : false,
12517     
12518     indicatorpos : 'left',
12519     
12520     labellg : 0,
12521     labelmd : 0,
12522     labelsm : 0,
12523     labelxs : 0,
12524     
12525     capture : '',
12526     accept : '',
12527     
12528     parentLabelAlign : function()
12529     {
12530         var parent = this;
12531         while (parent.parent()) {
12532             parent = parent.parent();
12533             if (typeof(parent.labelAlign) !='undefined') {
12534                 return parent.labelAlign;
12535             }
12536         }
12537         return 'left';
12538         
12539     },
12540     
12541     getAutoCreate : function()
12542     {
12543         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12544         
12545         var id = Roo.id();
12546         
12547         var cfg = {};
12548         
12549         if(this.inputType != 'hidden'){
12550             cfg.cls = 'form-group' //input-group
12551         }
12552         
12553         var input =  {
12554             tag: 'input',
12555             id : id,
12556             type : this.inputType,
12557             value : this.value,
12558             cls : 'form-control',
12559             placeholder : this.placeholder || '',
12560             autocomplete : this.autocomplete || 'new-password'
12561         };
12562         if (this.inputType == 'file') {
12563             input.style = 'overflow:hidden'; // why not in CSS?
12564         }
12565         
12566         if(this.capture.length){
12567             input.capture = this.capture;
12568         }
12569         
12570         if(this.accept.length){
12571             input.accept = this.accept + "/*";
12572         }
12573         
12574         if(this.align){
12575             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12576         }
12577         
12578         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12579             input.maxLength = this.maxLength;
12580         }
12581         
12582         if (this.disabled) {
12583             input.disabled=true;
12584         }
12585         
12586         if (this.readOnly) {
12587             input.readonly=true;
12588         }
12589         
12590         if (this.name) {
12591             input.name = this.name;
12592         }
12593         
12594         if (this.size) {
12595             input.cls += ' input-' + this.size;
12596         }
12597         
12598         var settings=this;
12599         ['xs','sm','md','lg'].map(function(size){
12600             if (settings[size]) {
12601                 cfg.cls += ' col-' + size + '-' + settings[size];
12602             }
12603         });
12604         
12605         var inputblock = input;
12606         
12607         var feedback = {
12608             tag: 'span',
12609             cls: 'glyphicon form-control-feedback'
12610         };
12611             
12612         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12613             
12614             inputblock = {
12615                 cls : 'has-feedback',
12616                 cn :  [
12617                     input,
12618                     feedback
12619                 ] 
12620             };  
12621         }
12622         
12623         if (this.before || this.after) {
12624             
12625             inputblock = {
12626                 cls : 'input-group',
12627                 cn :  [] 
12628             };
12629             
12630             if (this.before && typeof(this.before) == 'string') {
12631                 
12632                 inputblock.cn.push({
12633                     tag :'span',
12634                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12635                     html : this.before
12636                 });
12637             }
12638             if (this.before && typeof(this.before) == 'object') {
12639                 this.before = Roo.factory(this.before);
12640                 
12641                 inputblock.cn.push({
12642                     tag :'span',
12643                     cls : 'roo-input-before input-group-prepend   input-group-' +
12644                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12645                 });
12646             }
12647             
12648             inputblock.cn.push(input);
12649             
12650             if (this.after && typeof(this.after) == 'string') {
12651                 inputblock.cn.push({
12652                     tag :'span',
12653                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12654                     html : this.after
12655                 });
12656             }
12657             if (this.after && typeof(this.after) == 'object') {
12658                 this.after = Roo.factory(this.after);
12659                 
12660                 inputblock.cn.push({
12661                     tag :'span',
12662                     cls : 'roo-input-after input-group-append  input-group-' +
12663                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12664                 });
12665             }
12666             
12667             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12668                 inputblock.cls += ' has-feedback';
12669                 inputblock.cn.push(feedback);
12670             }
12671         };
12672         var indicator = {
12673             tag : 'i',
12674             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12675             tooltip : 'This field is required'
12676         };
12677         if (this.allowBlank ) {
12678             indicator.style = this.allowBlank ? ' display:none' : '';
12679         }
12680         if (align ==='left' && this.fieldLabel.length) {
12681             
12682             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12683             
12684             cfg.cn = [
12685                 indicator,
12686                 {
12687                     tag: 'label',
12688                     'for' :  id,
12689                     cls : 'control-label col-form-label',
12690                     html : this.fieldLabel
12691
12692                 },
12693                 {
12694                     cls : "", 
12695                     cn: [
12696                         inputblock
12697                     ]
12698                 }
12699             ];
12700             
12701             var labelCfg = cfg.cn[1];
12702             var contentCfg = cfg.cn[2];
12703             
12704             if(this.indicatorpos == 'right'){
12705                 cfg.cn = [
12706                     {
12707                         tag: 'label',
12708                         'for' :  id,
12709                         cls : 'control-label col-form-label',
12710                         cn : [
12711                             {
12712                                 tag : 'span',
12713                                 html : this.fieldLabel
12714                             },
12715                             indicator
12716                         ]
12717                     },
12718                     {
12719                         cls : "",
12720                         cn: [
12721                             inputblock
12722                         ]
12723                     }
12724
12725                 ];
12726                 
12727                 labelCfg = cfg.cn[0];
12728                 contentCfg = cfg.cn[1];
12729             
12730             }
12731             
12732             if(this.labelWidth > 12){
12733                 labelCfg.style = "width: " + this.labelWidth + 'px';
12734             }
12735             
12736             if(this.labelWidth < 13 && this.labelmd == 0){
12737                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12738             }
12739             
12740             if(this.labellg > 0){
12741                 labelCfg.cls += ' col-lg-' + this.labellg;
12742                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12743             }
12744             
12745             if(this.labelmd > 0){
12746                 labelCfg.cls += ' col-md-' + this.labelmd;
12747                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12748             }
12749             
12750             if(this.labelsm > 0){
12751                 labelCfg.cls += ' col-sm-' + this.labelsm;
12752                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12753             }
12754             
12755             if(this.labelxs > 0){
12756                 labelCfg.cls += ' col-xs-' + this.labelxs;
12757                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12758             }
12759             
12760             
12761         } else if ( this.fieldLabel.length) {
12762                 
12763             
12764             
12765             cfg.cn = [
12766                 {
12767                     tag : 'i',
12768                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12769                     tooltip : 'This field is required',
12770                     style : this.allowBlank ? ' display:none' : '' 
12771                 },
12772                 {
12773                     tag: 'label',
12774                    //cls : 'input-group-addon',
12775                     html : this.fieldLabel
12776
12777                 },
12778
12779                inputblock
12780
12781            ];
12782            
12783            if(this.indicatorpos == 'right'){
12784        
12785                 cfg.cn = [
12786                     {
12787                         tag: 'label',
12788                        //cls : 'input-group-addon',
12789                         html : this.fieldLabel
12790
12791                     },
12792                     {
12793                         tag : 'i',
12794                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12795                         tooltip : 'This field is required',
12796                         style : this.allowBlank ? ' display:none' : '' 
12797                     },
12798
12799                    inputblock
12800
12801                ];
12802
12803             }
12804
12805         } else {
12806             
12807             cfg.cn = [
12808
12809                     inputblock
12810
12811             ];
12812                 
12813                 
12814         };
12815         
12816         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12817            cfg.cls += ' navbar-form';
12818         }
12819         
12820         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12821             // on BS4 we do this only if not form 
12822             cfg.cls += ' navbar-form';
12823             cfg.tag = 'li';
12824         }
12825         
12826         return cfg;
12827         
12828     },
12829     /**
12830      * return the real input element.
12831      */
12832     inputEl: function ()
12833     {
12834         return this.el.select('input.form-control',true).first();
12835     },
12836     
12837     tooltipEl : function()
12838     {
12839         return this.inputEl();
12840     },
12841     
12842     indicatorEl : function()
12843     {
12844         if (Roo.bootstrap.version == 4) {
12845             return false; // not enabled in v4 yet.
12846         }
12847         
12848         var indicator = this.el.select('i.roo-required-indicator',true).first();
12849         
12850         if(!indicator){
12851             return false;
12852         }
12853         
12854         return indicator;
12855         
12856     },
12857     
12858     setDisabled : function(v)
12859     {
12860         var i  = this.inputEl().dom;
12861         if (!v) {
12862             i.removeAttribute('disabled');
12863             return;
12864             
12865         }
12866         i.setAttribute('disabled','true');
12867     },
12868     initEvents : function()
12869     {
12870           
12871         this.inputEl().on("keydown" , this.fireKey,  this);
12872         this.inputEl().on("focus", this.onFocus,  this);
12873         this.inputEl().on("blur", this.onBlur,  this);
12874         
12875         this.inputEl().relayEvent('keyup', this);
12876         this.inputEl().relayEvent('paste', this);
12877         
12878         this.indicator = this.indicatorEl();
12879         
12880         if(this.indicator){
12881             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12882         }
12883  
12884         // reference to original value for reset
12885         this.originalValue = this.getValue();
12886         //Roo.form.TextField.superclass.initEvents.call(this);
12887         if(this.validationEvent == 'keyup'){
12888             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12889             this.inputEl().on('keyup', this.filterValidation, this);
12890         }
12891         else if(this.validationEvent !== false){
12892             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12893         }
12894         
12895         if(this.selectOnFocus){
12896             this.on("focus", this.preFocus, this);
12897             
12898         }
12899         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12900             this.inputEl().on("keypress", this.filterKeys, this);
12901         } else {
12902             this.inputEl().relayEvent('keypress', this);
12903         }
12904        /* if(this.grow){
12905             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12906             this.el.on("click", this.autoSize,  this);
12907         }
12908         */
12909         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12910             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12911         }
12912         
12913         if (typeof(this.before) == 'object') {
12914             this.before.render(this.el.select('.roo-input-before',true).first());
12915         }
12916         if (typeof(this.after) == 'object') {
12917             this.after.render(this.el.select('.roo-input-after',true).first());
12918         }
12919         
12920         this.inputEl().on('change', this.onChange, this);
12921         
12922     },
12923     filterValidation : function(e){
12924         if(!e.isNavKeyPress()){
12925             this.validationTask.delay(this.validationDelay);
12926         }
12927     },
12928      /**
12929      * Validates the field value
12930      * @return {Boolean} True if the value is valid, else false
12931      */
12932     validate : function(){
12933         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12934         if(this.disabled || this.validateValue(this.getRawValue())){
12935             this.markValid();
12936             return true;
12937         }
12938         
12939         this.markInvalid();
12940         return false;
12941     },
12942     
12943     
12944     /**
12945      * Validates a value according to the field's validation rules and marks the field as invalid
12946      * if the validation fails
12947      * @param {Mixed} value The value to validate
12948      * @return {Boolean} True if the value is valid, else false
12949      */
12950     validateValue : function(value)
12951     {
12952         if(this.getVisibilityEl().hasClass('hidden')){
12953             return true;
12954         }
12955         
12956         if(value.length < 1)  { // if it's blank
12957             if(this.allowBlank){
12958                 return true;
12959             }
12960             return false;
12961         }
12962         
12963         if(value.length < this.minLength){
12964             return false;
12965         }
12966         if(value.length > this.maxLength){
12967             return false;
12968         }
12969         if(this.vtype){
12970             var vt = Roo.form.VTypes;
12971             if(!vt[this.vtype](value, this)){
12972                 return false;
12973             }
12974         }
12975         if(typeof this.validator == "function"){
12976             var msg = this.validator(value);
12977             if (typeof(msg) == 'string') {
12978                 this.invalidText = msg;
12979             }
12980             if(msg !== true){
12981                 return false;
12982             }
12983         }
12984         
12985         if(this.regex && !this.regex.test(value)){
12986             return false;
12987         }
12988         
12989         return true;
12990     },
12991     
12992      // private
12993     fireKey : function(e){
12994         //Roo.log('field ' + e.getKey());
12995         if(e.isNavKeyPress()){
12996             this.fireEvent("specialkey", this, e);
12997         }
12998     },
12999     focus : function (selectText){
13000         if(this.rendered){
13001             this.inputEl().focus();
13002             if(selectText === true){
13003                 this.inputEl().dom.select();
13004             }
13005         }
13006         return this;
13007     } ,
13008     
13009     onFocus : function(){
13010         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13011            // this.el.addClass(this.focusClass);
13012         }
13013         if(!this.hasFocus){
13014             this.hasFocus = true;
13015             this.startValue = this.getValue();
13016             this.fireEvent("focus", this);
13017         }
13018     },
13019     
13020     beforeBlur : Roo.emptyFn,
13021
13022     
13023     // private
13024     onBlur : function(){
13025         this.beforeBlur();
13026         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13027             //this.el.removeClass(this.focusClass);
13028         }
13029         this.hasFocus = false;
13030         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13031             this.validate();
13032         }
13033         var v = this.getValue();
13034         if(String(v) !== String(this.startValue)){
13035             this.fireEvent('change', this, v, this.startValue);
13036         }
13037         this.fireEvent("blur", this);
13038     },
13039     
13040     onChange : function(e)
13041     {
13042         var v = this.getValue();
13043         if(String(v) !== String(this.startValue)){
13044             this.fireEvent('change', this, v, this.startValue);
13045         }
13046         
13047     },
13048     
13049     /**
13050      * Resets the current field value to the originally loaded value and clears any validation messages
13051      */
13052     reset : function(){
13053         this.setValue(this.originalValue);
13054         this.validate();
13055     },
13056      /**
13057      * Returns the name of the field
13058      * @return {Mixed} name The name field
13059      */
13060     getName: function(){
13061         return this.name;
13062     },
13063      /**
13064      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13065      * @return {Mixed} value The field value
13066      */
13067     getValue : function(){
13068         
13069         var v = this.inputEl().getValue();
13070         
13071         return v;
13072     },
13073     /**
13074      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13075      * @return {Mixed} value The field value
13076      */
13077     getRawValue : function(){
13078         var v = this.inputEl().getValue();
13079         
13080         return v;
13081     },
13082     
13083     /**
13084      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13085      * @param {Mixed} value The value to set
13086      */
13087     setRawValue : function(v){
13088         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13089     },
13090     
13091     selectText : function(start, end){
13092         var v = this.getRawValue();
13093         if(v.length > 0){
13094             start = start === undefined ? 0 : start;
13095             end = end === undefined ? v.length : end;
13096             var d = this.inputEl().dom;
13097             if(d.setSelectionRange){
13098                 d.setSelectionRange(start, end);
13099             }else if(d.createTextRange){
13100                 var range = d.createTextRange();
13101                 range.moveStart("character", start);
13102                 range.moveEnd("character", v.length-end);
13103                 range.select();
13104             }
13105         }
13106     },
13107     
13108     /**
13109      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13110      * @param {Mixed} value The value to set
13111      */
13112     setValue : function(v){
13113         this.value = v;
13114         if(this.rendered){
13115             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13116             this.validate();
13117         }
13118     },
13119     
13120     /*
13121     processValue : function(value){
13122         if(this.stripCharsRe){
13123             var newValue = value.replace(this.stripCharsRe, '');
13124             if(newValue !== value){
13125                 this.setRawValue(newValue);
13126                 return newValue;
13127             }
13128         }
13129         return value;
13130     },
13131   */
13132     preFocus : function(){
13133         
13134         if(this.selectOnFocus){
13135             this.inputEl().dom.select();
13136         }
13137     },
13138     filterKeys : function(e){
13139         var k = e.getKey();
13140         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13141             return;
13142         }
13143         var c = e.getCharCode(), cc = String.fromCharCode(c);
13144         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13145             return;
13146         }
13147         if(!this.maskRe.test(cc)){
13148             e.stopEvent();
13149         }
13150     },
13151      /**
13152      * Clear any invalid styles/messages for this field
13153      */
13154     clearInvalid : function(){
13155         
13156         if(!this.el || this.preventMark){ // not rendered
13157             return;
13158         }
13159         
13160         
13161         this.el.removeClass([this.invalidClass, 'is-invalid']);
13162         
13163         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13164             
13165             var feedback = this.el.select('.form-control-feedback', true).first();
13166             
13167             if(feedback){
13168                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13169             }
13170             
13171         }
13172         
13173         if(this.indicator){
13174             this.indicator.removeClass('visible');
13175             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13176         }
13177         
13178         this.fireEvent('valid', this);
13179     },
13180     
13181      /**
13182      * Mark this field as valid
13183      */
13184     markValid : function()
13185     {
13186         if(!this.el  || this.preventMark){ // not rendered...
13187             return;
13188         }
13189         
13190         this.el.removeClass([this.invalidClass, this.validClass]);
13191         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13192
13193         var feedback = this.el.select('.form-control-feedback', true).first();
13194             
13195         if(feedback){
13196             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13197         }
13198         
13199         if(this.indicator){
13200             this.indicator.removeClass('visible');
13201             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13202         }
13203         
13204         if(this.disabled){
13205             return;
13206         }
13207         
13208            
13209         if(this.allowBlank && !this.getRawValue().length){
13210             return;
13211         }
13212         if (Roo.bootstrap.version == 3) {
13213             this.el.addClass(this.validClass);
13214         } else {
13215             this.inputEl().addClass('is-valid');
13216         }
13217
13218         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13219             
13220             var feedback = this.el.select('.form-control-feedback', true).first();
13221             
13222             if(feedback){
13223                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13224                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13225             }
13226             
13227         }
13228         
13229         this.fireEvent('valid', this);
13230     },
13231     
13232      /**
13233      * Mark this field as invalid
13234      * @param {String} msg The validation message
13235      */
13236     markInvalid : function(msg)
13237     {
13238         if(!this.el  || this.preventMark){ // not rendered
13239             return;
13240         }
13241         
13242         this.el.removeClass([this.invalidClass, this.validClass]);
13243         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13244         
13245         var feedback = this.el.select('.form-control-feedback', true).first();
13246             
13247         if(feedback){
13248             this.el.select('.form-control-feedback', true).first().removeClass(
13249                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13250         }
13251
13252         if(this.disabled){
13253             return;
13254         }
13255         
13256         if(this.allowBlank && !this.getRawValue().length){
13257             return;
13258         }
13259         
13260         if(this.indicator){
13261             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13262             this.indicator.addClass('visible');
13263         }
13264         if (Roo.bootstrap.version == 3) {
13265             this.el.addClass(this.invalidClass);
13266         } else {
13267             this.inputEl().addClass('is-invalid');
13268         }
13269         
13270         
13271         
13272         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13273             
13274             var feedback = this.el.select('.form-control-feedback', true).first();
13275             
13276             if(feedback){
13277                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13278                 
13279                 if(this.getValue().length || this.forceFeedback){
13280                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13281                 }
13282                 
13283             }
13284             
13285         }
13286         
13287         this.fireEvent('invalid', this, msg);
13288     },
13289     // private
13290     SafariOnKeyDown : function(event)
13291     {
13292         // this is a workaround for a password hang bug on chrome/ webkit.
13293         if (this.inputEl().dom.type != 'password') {
13294             return;
13295         }
13296         
13297         var isSelectAll = false;
13298         
13299         if(this.inputEl().dom.selectionEnd > 0){
13300             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13301         }
13302         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13303             event.preventDefault();
13304             this.setValue('');
13305             return;
13306         }
13307         
13308         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13309             
13310             event.preventDefault();
13311             // this is very hacky as keydown always get's upper case.
13312             //
13313             var cc = String.fromCharCode(event.getCharCode());
13314             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13315             
13316         }
13317     },
13318     adjustWidth : function(tag, w){
13319         tag = tag.toLowerCase();
13320         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13321             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13322                 if(tag == 'input'){
13323                     return w + 2;
13324                 }
13325                 if(tag == 'textarea'){
13326                     return w-2;
13327                 }
13328             }else if(Roo.isOpera){
13329                 if(tag == 'input'){
13330                     return w + 2;
13331                 }
13332                 if(tag == 'textarea'){
13333                     return w-2;
13334                 }
13335             }
13336         }
13337         return w;
13338     },
13339     
13340     setFieldLabel : function(v)
13341     {
13342         if(!this.rendered){
13343             return;
13344         }
13345         
13346         if(this.indicatorEl()){
13347             var ar = this.el.select('label > span',true);
13348             
13349             if (ar.elements.length) {
13350                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13351                 this.fieldLabel = v;
13352                 return;
13353             }
13354             
13355             var br = this.el.select('label',true);
13356             
13357             if(br.elements.length) {
13358                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13359                 this.fieldLabel = v;
13360                 return;
13361             }
13362             
13363             Roo.log('Cannot Found any of label > span || label in input');
13364             return;
13365         }
13366         
13367         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13368         this.fieldLabel = v;
13369         
13370         
13371     }
13372 });
13373
13374  
13375 /*
13376  * - LGPL
13377  *
13378  * Input
13379  * 
13380  */
13381
13382 /**
13383  * @class Roo.bootstrap.form.TextArea
13384  * @extends Roo.bootstrap.form.Input
13385  * Bootstrap TextArea class
13386  * @cfg {Number} cols Specifies the visible width of a text area
13387  * @cfg {Number} rows Specifies the visible number of lines in a text area
13388  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13389  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13390  * @cfg {string} html text
13391  * 
13392  * @constructor
13393  * Create a new TextArea
13394  * @param {Object} config The config object
13395  */
13396
13397 Roo.bootstrap.form.TextArea = function(config){
13398     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13399    
13400 };
13401
13402 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13403      
13404     cols : false,
13405     rows : 5,
13406     readOnly : false,
13407     warp : 'soft',
13408     resize : false,
13409     value: false,
13410     html: false,
13411     
13412     getAutoCreate : function(){
13413         
13414         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13415         
13416         var id = Roo.id();
13417         
13418         var cfg = {};
13419         
13420         if(this.inputType != 'hidden'){
13421             cfg.cls = 'form-group' //input-group
13422         }
13423         
13424         var input =  {
13425             tag: 'textarea',
13426             id : id,
13427             warp : this.warp,
13428             rows : this.rows,
13429             value : this.value || '',
13430             html: this.html || '',
13431             cls : 'form-control',
13432             placeholder : this.placeholder || '' 
13433             
13434         };
13435         
13436         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13437             input.maxLength = this.maxLength;
13438         }
13439         
13440         if(this.resize){
13441             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13442         }
13443         
13444         if(this.cols){
13445             input.cols = this.cols;
13446         }
13447         
13448         if (this.readOnly) {
13449             input.readonly = true;
13450         }
13451         
13452         if (this.name) {
13453             input.name = this.name;
13454         }
13455         
13456         if (this.size) {
13457             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13458         }
13459         
13460         var settings=this;
13461         ['xs','sm','md','lg'].map(function(size){
13462             if (settings[size]) {
13463                 cfg.cls += ' col-' + size + '-' + settings[size];
13464             }
13465         });
13466         
13467         var inputblock = input;
13468         
13469         if(this.hasFeedback && !this.allowBlank){
13470             
13471             var feedback = {
13472                 tag: 'span',
13473                 cls: 'glyphicon form-control-feedback'
13474             };
13475
13476             inputblock = {
13477                 cls : 'has-feedback',
13478                 cn :  [
13479                     input,
13480                     feedback
13481                 ] 
13482             };  
13483         }
13484         
13485         
13486         if (this.before || this.after) {
13487             
13488             inputblock = {
13489                 cls : 'input-group',
13490                 cn :  [] 
13491             };
13492             if (this.before) {
13493                 inputblock.cn.push({
13494                     tag :'span',
13495                     cls : 'input-group-addon',
13496                     html : this.before
13497                 });
13498             }
13499             
13500             inputblock.cn.push(input);
13501             
13502             if(this.hasFeedback && !this.allowBlank){
13503                 inputblock.cls += ' has-feedback';
13504                 inputblock.cn.push(feedback);
13505             }
13506             
13507             if (this.after) {
13508                 inputblock.cn.push({
13509                     tag :'span',
13510                     cls : 'input-group-addon',
13511                     html : this.after
13512                 });
13513             }
13514             
13515         }
13516         
13517         if (align ==='left' && this.fieldLabel.length) {
13518             cfg.cn = [
13519                 {
13520                     tag: 'label',
13521                     'for' :  id,
13522                     cls : 'control-label',
13523                     html : this.fieldLabel
13524                 },
13525                 {
13526                     cls : "",
13527                     cn: [
13528                         inputblock
13529                     ]
13530                 }
13531
13532             ];
13533             
13534             if(this.labelWidth > 12){
13535                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13536             }
13537
13538             if(this.labelWidth < 13 && this.labelmd == 0){
13539                 this.labelmd = this.labelWidth;
13540             }
13541
13542             if(this.labellg > 0){
13543                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13544                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13545             }
13546
13547             if(this.labelmd > 0){
13548                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13549                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13550             }
13551
13552             if(this.labelsm > 0){
13553                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13554                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13555             }
13556
13557             if(this.labelxs > 0){
13558                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13559                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13560             }
13561             
13562         } else if ( this.fieldLabel.length) {
13563             cfg.cn = [
13564
13565                {
13566                    tag: 'label',
13567                    //cls : 'input-group-addon',
13568                    html : this.fieldLabel
13569
13570                },
13571
13572                inputblock
13573
13574            ];
13575
13576         } else {
13577
13578             cfg.cn = [
13579
13580                 inputblock
13581
13582             ];
13583                 
13584         }
13585         
13586         if (this.disabled) {
13587             input.disabled=true;
13588         }
13589         
13590         return cfg;
13591         
13592     },
13593     /**
13594      * return the real textarea element.
13595      */
13596     inputEl: function ()
13597     {
13598         return this.el.select('textarea.form-control',true).first();
13599     },
13600     
13601     /**
13602      * Clear any invalid styles/messages for this field
13603      */
13604     clearInvalid : function()
13605     {
13606         
13607         if(!this.el || this.preventMark){ // not rendered
13608             return;
13609         }
13610         
13611         var label = this.el.select('label', true).first();
13612         var icon = this.el.select('i.fa-star', true).first();
13613         
13614         if(label && icon){
13615             icon.remove();
13616         }
13617         this.el.removeClass( this.validClass);
13618         this.inputEl().removeClass('is-invalid');
13619          
13620         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13621             
13622             var feedback = this.el.select('.form-control-feedback', true).first();
13623             
13624             if(feedback){
13625                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13626             }
13627             
13628         }
13629         
13630         this.fireEvent('valid', this);
13631     },
13632     
13633      /**
13634      * Mark this field as valid
13635      */
13636     markValid : function()
13637     {
13638         if(!this.el  || this.preventMark){ // not rendered
13639             return;
13640         }
13641         
13642         this.el.removeClass([this.invalidClass, this.validClass]);
13643         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13644         
13645         var feedback = this.el.select('.form-control-feedback', true).first();
13646             
13647         if(feedback){
13648             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13649         }
13650
13651         if(this.disabled || this.allowBlank){
13652             return;
13653         }
13654         
13655         var label = this.el.select('label', true).first();
13656         var icon = this.el.select('i.fa-star', true).first();
13657         
13658         if(label && icon){
13659             icon.remove();
13660         }
13661         if (Roo.bootstrap.version == 3) {
13662             this.el.addClass(this.validClass);
13663         } else {
13664             this.inputEl().addClass('is-valid');
13665         }
13666         
13667         
13668         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13669             
13670             var feedback = this.el.select('.form-control-feedback', true).first();
13671             
13672             if(feedback){
13673                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13674                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13675             }
13676             
13677         }
13678         
13679         this.fireEvent('valid', this);
13680     },
13681     
13682      /**
13683      * Mark this field as invalid
13684      * @param {String} msg The validation message
13685      */
13686     markInvalid : function(msg)
13687     {
13688         if(!this.el  || this.preventMark){ // not rendered
13689             return;
13690         }
13691         
13692         this.el.removeClass([this.invalidClass, this.validClass]);
13693         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13694         
13695         var feedback = this.el.select('.form-control-feedback', true).first();
13696             
13697         if(feedback){
13698             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13699         }
13700
13701         if(this.disabled || this.allowBlank){
13702             return;
13703         }
13704         
13705         var label = this.el.select('label', true).first();
13706         var icon = this.el.select('i.fa-star', true).first();
13707         
13708         if(!this.getValue().length && label && !icon){
13709             this.el.createChild({
13710                 tag : 'i',
13711                 cls : 'text-danger fa fa-lg fa-star',
13712                 tooltip : 'This field is required',
13713                 style : 'margin-right:5px;'
13714             }, label, true);
13715         }
13716         
13717         if (Roo.bootstrap.version == 3) {
13718             this.el.addClass(this.invalidClass);
13719         } else {
13720             this.inputEl().addClass('is-invalid');
13721         }
13722         
13723         // fixme ... this may be depricated need to test..
13724         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13725             
13726             var feedback = this.el.select('.form-control-feedback', true).first();
13727             
13728             if(feedback){
13729                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13730                 
13731                 if(this.getValue().length || this.forceFeedback){
13732                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13733                 }
13734                 
13735             }
13736             
13737         }
13738         
13739         this.fireEvent('invalid', this, msg);
13740     }
13741 });
13742
13743  
13744 /*
13745  * - LGPL
13746  *
13747  * trigger field - base class for combo..
13748  * 
13749  */
13750  
13751 /**
13752  * @class Roo.bootstrap.form.TriggerField
13753  * @extends Roo.bootstrap.form.Input
13754  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13755  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13756  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13757  * for which you can provide a custom implementation.  For example:
13758  * <pre><code>
13759 var trigger = new Roo.bootstrap.form.TriggerField();
13760 trigger.onTriggerClick = myTriggerFn;
13761 trigger.applyTo('my-field');
13762 </code></pre>
13763  *
13764  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13765  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13766  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13767  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13768  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13769
13770  * @constructor
13771  * Create a new TriggerField.
13772  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13773  * to the base TextField)
13774  */
13775 Roo.bootstrap.form.TriggerField = function(config){
13776     this.mimicing = false;
13777     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13778 };
13779
13780 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13781     /**
13782      * @cfg {String} triggerClass A CSS class to apply to the trigger
13783      */
13784      /**
13785      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13786      */
13787     hideTrigger:false,
13788
13789     /**
13790      * @cfg {Boolean} removable (true|false) special filter default false
13791      */
13792     removable : false,
13793     
13794     /** @cfg {Boolean} grow @hide */
13795     /** @cfg {Number} growMin @hide */
13796     /** @cfg {Number} growMax @hide */
13797
13798     /**
13799      * @hide 
13800      * @method
13801      */
13802     autoSize: Roo.emptyFn,
13803     // private
13804     monitorTab : true,
13805     // private
13806     deferHeight : true,
13807
13808     
13809     actionMode : 'wrap',
13810     
13811     caret : false,
13812     
13813     
13814     getAutoCreate : function(){
13815        
13816         var align = this.labelAlign || this.parentLabelAlign();
13817         
13818         var id = Roo.id();
13819         
13820         var cfg = {
13821             cls: 'form-group' //input-group
13822         };
13823         
13824         
13825         var input =  {
13826             tag: 'input',
13827             id : id,
13828             type : this.inputType,
13829             cls : 'form-control',
13830             autocomplete: 'new-password',
13831             placeholder : this.placeholder || '' 
13832             
13833         };
13834         if (this.name) {
13835             input.name = this.name;
13836         }
13837         if (this.size) {
13838             input.cls += ' input-' + this.size;
13839         }
13840         
13841         if (this.disabled) {
13842             input.disabled=true;
13843         }
13844         
13845         var inputblock = input;
13846         
13847         if(this.hasFeedback && !this.allowBlank){
13848             
13849             var feedback = {
13850                 tag: 'span',
13851                 cls: 'glyphicon form-control-feedback'
13852             };
13853             
13854             if(this.removable && !this.editable  ){
13855                 inputblock = {
13856                     cls : 'has-feedback',
13857                     cn :  [
13858                         inputblock,
13859                         {
13860                             tag: 'button',
13861                             html : 'x',
13862                             cls : 'roo-combo-removable-btn close'
13863                         },
13864                         feedback
13865                     ] 
13866                 };
13867             } else {
13868                 inputblock = {
13869                     cls : 'has-feedback',
13870                     cn :  [
13871                         inputblock,
13872                         feedback
13873                     ] 
13874                 };
13875             }
13876
13877         } else {
13878             if(this.removable && !this.editable ){
13879                 inputblock = {
13880                     cls : 'roo-removable',
13881                     cn :  [
13882                         inputblock,
13883                         {
13884                             tag: 'button',
13885                             html : 'x',
13886                             cls : 'roo-combo-removable-btn close'
13887                         }
13888                     ] 
13889                 };
13890             }
13891         }
13892         
13893         if (this.before || this.after) {
13894             
13895             inputblock = {
13896                 cls : 'input-group',
13897                 cn :  [] 
13898             };
13899             if (this.before) {
13900                 inputblock.cn.push({
13901                     tag :'span',
13902                     cls : 'input-group-addon input-group-prepend input-group-text',
13903                     html : this.before
13904                 });
13905             }
13906             
13907             inputblock.cn.push(input);
13908             
13909             if(this.hasFeedback && !this.allowBlank){
13910                 inputblock.cls += ' has-feedback';
13911                 inputblock.cn.push(feedback);
13912             }
13913             
13914             if (this.after) {
13915                 inputblock.cn.push({
13916                     tag :'span',
13917                     cls : 'input-group-addon input-group-append input-group-text',
13918                     html : this.after
13919                 });
13920             }
13921             
13922         };
13923         
13924       
13925         
13926         var ibwrap = inputblock;
13927         
13928         if(this.multiple){
13929             ibwrap = {
13930                 tag: 'ul',
13931                 cls: 'roo-select2-choices',
13932                 cn:[
13933                     {
13934                         tag: 'li',
13935                         cls: 'roo-select2-search-field',
13936                         cn: [
13937
13938                             inputblock
13939                         ]
13940                     }
13941                 ]
13942             };
13943                 
13944         }
13945         
13946         var combobox = {
13947             cls: 'roo-select2-container input-group',
13948             cn: [
13949                  {
13950                     tag: 'input',
13951                     type : 'hidden',
13952                     cls: 'form-hidden-field'
13953                 },
13954                 ibwrap
13955             ]
13956         };
13957         
13958         if(!this.multiple && this.showToggleBtn){
13959             
13960             var caret = {
13961                         tag: 'span',
13962                         cls: 'caret'
13963              };
13964             if (this.caret != false) {
13965                 caret = {
13966                      tag: 'i',
13967                      cls: 'fa fa-' + this.caret
13968                 };
13969                 
13970             }
13971             
13972             combobox.cn.push({
13973                 tag :'span',
13974                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13975                 cn : [
13976                     Roo.bootstrap.version == 3 ? caret : '',
13977                     {
13978                         tag: 'span',
13979                         cls: 'combobox-clear',
13980                         cn  : [
13981                             {
13982                                 tag : 'i',
13983                                 cls: 'icon-remove'
13984                             }
13985                         ]
13986                     }
13987                 ]
13988
13989             })
13990         }
13991         
13992         if(this.multiple){
13993             combobox.cls += ' roo-select2-container-multi';
13994         }
13995          var indicator = {
13996             tag : 'i',
13997             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13998             tooltip : 'This field is required'
13999         };
14000         if (Roo.bootstrap.version == 4) {
14001             indicator = {
14002                 tag : 'i',
14003                 style : 'display:none'
14004             };
14005         }
14006         
14007         
14008         if (align ==='left' && this.fieldLabel.length) {
14009             
14010             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
14011
14012             cfg.cn = [
14013                 indicator,
14014                 {
14015                     tag: 'label',
14016                     'for' :  id,
14017                     cls : 'control-label',
14018                     html : this.fieldLabel
14019
14020                 },
14021                 {
14022                     cls : "", 
14023                     cn: [
14024                         combobox
14025                     ]
14026                 }
14027
14028             ];
14029             
14030             var labelCfg = cfg.cn[1];
14031             var contentCfg = cfg.cn[2];
14032             
14033             if(this.indicatorpos == 'right'){
14034                 cfg.cn = [
14035                     {
14036                         tag: 'label',
14037                         'for' :  id,
14038                         cls : 'control-label',
14039                         cn : [
14040                             {
14041                                 tag : 'span',
14042                                 html : this.fieldLabel
14043                             },
14044                             indicator
14045                         ]
14046                     },
14047                     {
14048                         cls : "", 
14049                         cn: [
14050                             combobox
14051                         ]
14052                     }
14053
14054                 ];
14055                 
14056                 labelCfg = cfg.cn[0];
14057                 contentCfg = cfg.cn[1];
14058             }
14059             
14060             if(this.labelWidth > 12){
14061                 labelCfg.style = "width: " + this.labelWidth + 'px';
14062             }
14063             
14064             if(this.labelWidth < 13 && this.labelmd == 0){
14065                 this.labelmd = this.labelWidth;
14066             }
14067             
14068             if(this.labellg > 0){
14069                 labelCfg.cls += ' col-lg-' + this.labellg;
14070                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14071             }
14072             
14073             if(this.labelmd > 0){
14074                 labelCfg.cls += ' col-md-' + this.labelmd;
14075                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14076             }
14077             
14078             if(this.labelsm > 0){
14079                 labelCfg.cls += ' col-sm-' + this.labelsm;
14080                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14081             }
14082             
14083             if(this.labelxs > 0){
14084                 labelCfg.cls += ' col-xs-' + this.labelxs;
14085                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14086             }
14087             
14088         } else if ( this.fieldLabel.length) {
14089 //                Roo.log(" label");
14090             cfg.cn = [
14091                 indicator,
14092                {
14093                    tag: 'label',
14094                    //cls : 'input-group-addon',
14095                    html : this.fieldLabel
14096
14097                },
14098
14099                combobox
14100
14101             ];
14102             
14103             if(this.indicatorpos == 'right'){
14104                 
14105                 cfg.cn = [
14106                     {
14107                        tag: 'label',
14108                        cn : [
14109                            {
14110                                tag : 'span',
14111                                html : this.fieldLabel
14112                            },
14113                            indicator
14114                        ]
14115
14116                     },
14117                     combobox
14118
14119                 ];
14120
14121             }
14122
14123         } else {
14124             
14125 //                Roo.log(" no label && no align");
14126                 cfg = combobox
14127                      
14128                 
14129         }
14130         
14131         var settings=this;
14132         ['xs','sm','md','lg'].map(function(size){
14133             if (settings[size]) {
14134                 cfg.cls += ' col-' + size + '-' + settings[size];
14135             }
14136         });
14137         
14138         return cfg;
14139         
14140     },
14141     
14142     
14143     
14144     // private
14145     onResize : function(w, h){
14146 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14147 //        if(typeof w == 'number'){
14148 //            var x = w - this.trigger.getWidth();
14149 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14150 //            this.trigger.setStyle('left', x+'px');
14151 //        }
14152     },
14153
14154     // private
14155     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14156
14157     // private
14158     getResizeEl : function(){
14159         return this.inputEl();
14160     },
14161
14162     // private
14163     getPositionEl : function(){
14164         return this.inputEl();
14165     },
14166
14167     // private
14168     alignErrorIcon : function(){
14169         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14170     },
14171
14172     // private
14173     initEvents : function(){
14174         
14175         this.createList();
14176         
14177         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14178         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14179         if(!this.multiple && this.showToggleBtn){
14180             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14181             if(this.hideTrigger){
14182                 this.trigger.setDisplayed(false);
14183             }
14184             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14185         }
14186         
14187         if(this.multiple){
14188             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14189         }
14190         
14191         if(this.removable && !this.editable && !this.tickable){
14192             var close = this.closeTriggerEl();
14193             
14194             if(close){
14195                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14196                 close.on('click', this.removeBtnClick, this, close);
14197             }
14198         }
14199         
14200         //this.trigger.addClassOnOver('x-form-trigger-over');
14201         //this.trigger.addClassOnClick('x-form-trigger-click');
14202         
14203         //if(!this.width){
14204         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14205         //}
14206     },
14207     
14208     closeTriggerEl : function()
14209     {
14210         var close = this.el.select('.roo-combo-removable-btn', true).first();
14211         return close ? close : false;
14212     },
14213     
14214     removeBtnClick : function(e, h, el)
14215     {
14216         e.preventDefault();
14217         
14218         if(this.fireEvent("remove", this) !== false){
14219             this.reset();
14220             this.fireEvent("afterremove", this)
14221         }
14222     },
14223     
14224     createList : function()
14225     {
14226         this.list = Roo.get(document.body).createChild({
14227             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14228             cls: 'typeahead typeahead-long dropdown-menu shadow',
14229             style: 'display:none'
14230         });
14231         
14232         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14233         
14234     },
14235
14236     // private
14237     initTrigger : function(){
14238        
14239     },
14240
14241     // private
14242     onDestroy : function(){
14243         if(this.trigger){
14244             this.trigger.removeAllListeners();
14245           //  this.trigger.remove();
14246         }
14247         //if(this.wrap){
14248         //    this.wrap.remove();
14249         //}
14250         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14251     },
14252
14253     // private
14254     onFocus : function(){
14255         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14256         /*
14257         if(!this.mimicing){
14258             this.wrap.addClass('x-trigger-wrap-focus');
14259             this.mimicing = true;
14260             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14261             if(this.monitorTab){
14262                 this.el.on("keydown", this.checkTab, this);
14263             }
14264         }
14265         */
14266     },
14267
14268     // private
14269     checkTab : function(e){
14270         if(e.getKey() == e.TAB){
14271             this.triggerBlur();
14272         }
14273     },
14274
14275     // private
14276     onBlur : function(){
14277         // do nothing
14278     },
14279
14280     // private
14281     mimicBlur : function(e, t){
14282         /*
14283         if(!this.wrap.contains(t) && this.validateBlur()){
14284             this.triggerBlur();
14285         }
14286         */
14287     },
14288
14289     // private
14290     triggerBlur : function(){
14291         this.mimicing = false;
14292         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14293         if(this.monitorTab){
14294             this.el.un("keydown", this.checkTab, this);
14295         }
14296         //this.wrap.removeClass('x-trigger-wrap-focus');
14297         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14298     },
14299
14300     // private
14301     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14302     validateBlur : function(e, t){
14303         return true;
14304     },
14305
14306     // private
14307     onDisable : function(){
14308         this.inputEl().dom.disabled = true;
14309         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14310         //if(this.wrap){
14311         //    this.wrap.addClass('x-item-disabled');
14312         //}
14313     },
14314
14315     // private
14316     onEnable : function(){
14317         this.inputEl().dom.disabled = false;
14318         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14319         //if(this.wrap){
14320         //    this.el.removeClass('x-item-disabled');
14321         //}
14322     },
14323
14324     // private
14325     onShow : function(){
14326         var ae = this.getActionEl();
14327         
14328         if(ae){
14329             ae.dom.style.display = '';
14330             ae.dom.style.visibility = 'visible';
14331         }
14332     },
14333
14334     // private
14335     
14336     onHide : function(){
14337         var ae = this.getActionEl();
14338         ae.dom.style.display = 'none';
14339     },
14340
14341     /**
14342      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14343      * by an implementing function.
14344      * @method
14345      * @param {EventObject} e
14346      */
14347     onTriggerClick : Roo.emptyFn
14348 });
14349  
14350 /*
14351 * Licence: LGPL
14352 */
14353
14354 /**
14355  * @class Roo.bootstrap.form.CardUploader
14356  * @extends Roo.bootstrap.Button
14357  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14358  * @cfg {Number} errorTimeout default 3000
14359  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14360  * @cfg {Array}  html The button text.
14361
14362  *
14363  * @constructor
14364  * Create a new CardUploader
14365  * @param {Object} config The config object
14366  */
14367
14368 Roo.bootstrap.form.CardUploader = function(config){
14369     
14370  
14371     
14372     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14373     
14374     
14375     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14376         return r.data.id
14377      });
14378     
14379      this.addEvents({
14380          // raw events
14381         /**
14382          * @event preview
14383          * When a image is clicked on - and needs to display a slideshow or similar..
14384          * @param {Roo.bootstrap.Card} this
14385          * @param {Object} The image information data 
14386          *
14387          */
14388         'preview' : true,
14389          /**
14390          * @event download
14391          * When a the download link is clicked
14392          * @param {Roo.bootstrap.Card} this
14393          * @param {Object} The image information data  contains 
14394          */
14395         'download' : true
14396         
14397     });
14398 };
14399  
14400 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14401     
14402      
14403     errorTimeout : 3000,
14404      
14405     images : false,
14406    
14407     fileCollection : false,
14408     allowBlank : true,
14409     
14410     getAutoCreate : function()
14411     {
14412         
14413         var cfg =  {
14414             cls :'form-group' ,
14415             cn : [
14416                
14417                 {
14418                     tag: 'label',
14419                    //cls : 'input-group-addon',
14420                     html : this.fieldLabel
14421
14422                 },
14423
14424                 {
14425                     tag: 'input',
14426                     type : 'hidden',
14427                     name : this.name,
14428                     value : this.value,
14429                     cls : 'd-none  form-control'
14430                 },
14431                 
14432                 {
14433                     tag: 'input',
14434                     multiple : 'multiple',
14435                     type : 'file',
14436                     cls : 'd-none  roo-card-upload-selector'
14437                 },
14438                 
14439                 {
14440                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14441                 },
14442                 {
14443                     cls : 'card-columns roo-card-uploader-container'
14444                 }
14445
14446             ]
14447         };
14448            
14449          
14450         return cfg;
14451     },
14452     
14453     getChildContainer : function() /// what children are added to.
14454     {
14455         return this.containerEl;
14456     },
14457    
14458     getButtonContainer : function() /// what children are added to.
14459     {
14460         return this.el.select(".roo-card-uploader-button-container").first();
14461     },
14462    
14463     initEvents : function()
14464     {
14465         
14466         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14467         
14468         var t = this;
14469         this.addxtype({
14470             xns: Roo.bootstrap,
14471
14472             xtype : 'Button',
14473             container_method : 'getButtonContainer' ,            
14474             html :  this.html, // fix changable?
14475             cls : 'w-100 ',
14476             listeners : {
14477                 'click' : function(btn, e) {
14478                     t.onClick(e);
14479                 }
14480             }
14481         });
14482         
14483         
14484         
14485         
14486         this.urlAPI = (window.createObjectURL && window) || 
14487                                 (window.URL && URL.revokeObjectURL && URL) || 
14488                                 (window.webkitURL && webkitURL);
14489                         
14490          
14491          
14492          
14493         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14494         
14495         this.selectorEl.on('change', this.onFileSelected, this);
14496         if (this.images) {
14497             var t = this;
14498             this.images.forEach(function(img) {
14499                 t.addCard(img)
14500             });
14501             this.images = false;
14502         }
14503         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14504          
14505        
14506     },
14507     
14508    
14509     onClick : function(e)
14510     {
14511         e.preventDefault();
14512          
14513         this.selectorEl.dom.click();
14514          
14515     },
14516     
14517     onFileSelected : function(e)
14518     {
14519         e.preventDefault();
14520         
14521         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14522             return;
14523         }
14524         
14525         Roo.each(this.selectorEl.dom.files, function(file){    
14526             this.addFile(file);
14527         }, this);
14528          
14529     },
14530     
14531       
14532     
14533       
14534     
14535     addFile : function(file)
14536     {
14537            
14538         if(typeof(file) === 'string'){
14539             throw "Add file by name?"; // should not happen
14540             return;
14541         }
14542         
14543         if(!file || !this.urlAPI){
14544             return;
14545         }
14546         
14547         // file;
14548         // file.type;
14549         
14550         var _this = this;
14551         
14552         
14553         var url = _this.urlAPI.createObjectURL( file);
14554            
14555         this.addCard({
14556             id : Roo.bootstrap.form.CardUploader.ID--,
14557             is_uploaded : false,
14558             src : url,
14559             srcfile : file,
14560             title : file.name,
14561             mimetype : file.type,
14562             preview : false,
14563             is_deleted : 0
14564         });
14565         
14566     },
14567     
14568     /**
14569      * addCard - add an Attachment to the uploader
14570      * @param data - the data about the image to upload
14571      *
14572      * {
14573           id : 123
14574           title : "Title of file",
14575           is_uploaded : false,
14576           src : "http://.....",
14577           srcfile : { the File upload object },
14578           mimetype : file.type,
14579           preview : false,
14580           is_deleted : 0
14581           .. any other data...
14582         }
14583      *
14584      * 
14585     */
14586     
14587     addCard : function (data)
14588     {
14589         // hidden input element?
14590         // if the file is not an image...
14591         //then we need to use something other that and header_image
14592         var t = this;
14593         //   remove.....
14594         var footer = [
14595             {
14596                 xns : Roo.bootstrap,
14597                 xtype : 'CardFooter',
14598                  items: [
14599                     {
14600                         xns : Roo.bootstrap,
14601                         xtype : 'Element',
14602                         cls : 'd-flex',
14603                         items : [
14604                             
14605                             {
14606                                 xns : Roo.bootstrap,
14607                                 xtype : 'Button',
14608                                 html : String.format("<small>{0}</small>", data.title),
14609                                 cls : 'col-10 text-left',
14610                                 size: 'sm',
14611                                 weight: 'link',
14612                                 fa : 'download',
14613                                 listeners : {
14614                                     click : function() {
14615                                      
14616                                         t.fireEvent( "download", t, data );
14617                                     }
14618                                 }
14619                             },
14620                           
14621                             {
14622                                 xns : Roo.bootstrap,
14623                                 xtype : 'Button',
14624                                 style: 'max-height: 28px; ',
14625                                 size : 'sm',
14626                                 weight: 'danger',
14627                                 cls : 'col-2',
14628                                 fa : 'times',
14629                                 listeners : {
14630                                     click : function() {
14631                                         t.removeCard(data.id)
14632                                     }
14633                                 }
14634                             }
14635                         ]
14636                     }
14637                     
14638                 ] 
14639             }
14640             
14641         ];
14642         
14643         var cn = this.addxtype(
14644             {
14645                  
14646                 xns : Roo.bootstrap,
14647                 xtype : 'Card',
14648                 closeable : true,
14649                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14650                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14651                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14652                 data : data,
14653                 html : false,
14654                  
14655                 items : footer,
14656                 initEvents : function() {
14657                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14658                     var card = this;
14659                     this.imgEl = this.el.select('.card-img-top').first();
14660                     if (this.imgEl) {
14661                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14662                         this.imgEl.set({ 'pointer' : 'cursor' });
14663                                   
14664                     }
14665                     this.getCardFooter().addClass('p-1');
14666                     
14667                   
14668                 }
14669                 
14670             }
14671         );
14672         // dont' really need ot update items.
14673         // this.items.push(cn);
14674         this.fileCollection.add(cn);
14675         
14676         if (!data.srcfile) {
14677             this.updateInput();
14678             return;
14679         }
14680             
14681         var _t = this;
14682         var reader = new FileReader();
14683         reader.addEventListener("load", function() {  
14684             data.srcdata =  reader.result;
14685             _t.updateInput();
14686         });
14687         reader.readAsDataURL(data.srcfile);
14688         
14689         
14690         
14691     },
14692     removeCard : function(id)
14693     {
14694         
14695         var card  = this.fileCollection.get(id);
14696         card.data.is_deleted = 1;
14697         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14698         //this.fileCollection.remove(card);
14699         //this.items = this.items.filter(function(e) { return e != card });
14700         // dont' really need ot update items.
14701         card.el.dom.parentNode.removeChild(card.el.dom);
14702         this.updateInput();
14703
14704         
14705     },
14706     reset: function()
14707     {
14708         this.fileCollection.each(function(card) {
14709             if (card.el.dom && card.el.dom.parentNode) {
14710                 card.el.dom.parentNode.removeChild(card.el.dom);
14711             }
14712         });
14713         this.fileCollection.clear();
14714         this.updateInput();
14715     },
14716     
14717     updateInput : function()
14718     {
14719          var data = [];
14720         this.fileCollection.each(function(e) {
14721             data.push(e.data);
14722             
14723         });
14724         this.inputEl().dom.value = JSON.stringify(data);
14725         
14726         
14727         
14728     }
14729     
14730     
14731 });
14732
14733
14734 Roo.bootstrap.form.CardUploader.ID = -1;/*
14735  * Based on:
14736  * Ext JS Library 1.1.1
14737  * Copyright(c) 2006-2007, Ext JS, LLC.
14738  *
14739  * Originally Released Under LGPL - original licence link has changed is not relivant.
14740  *
14741  * Fork - LGPL
14742  * <script type="text/javascript">
14743  */
14744
14745
14746 /**
14747  * @class Roo.data.SortTypes
14748  * @static
14749  * Defines the default sorting (casting?) comparison functions used when sorting data.
14750  */
14751 Roo.data.SortTypes = {
14752     /**
14753      * Default sort that does nothing
14754      * @param {Mixed} s The value being converted
14755      * @return {Mixed} The comparison value
14756      */
14757     none : function(s){
14758         return s;
14759     },
14760     
14761     /**
14762      * The regular expression used to strip tags
14763      * @type {RegExp}
14764      * @property
14765      */
14766     stripTagsRE : /<\/?[^>]+>/gi,
14767     
14768     /**
14769      * Strips all HTML tags to sort on text only
14770      * @param {Mixed} s The value being converted
14771      * @return {String} The comparison value
14772      */
14773     asText : function(s){
14774         return String(s).replace(this.stripTagsRE, "");
14775     },
14776     
14777     /**
14778      * Strips all HTML tags to sort on text only - Case insensitive
14779      * @param {Mixed} s The value being converted
14780      * @return {String} The comparison value
14781      */
14782     asUCText : function(s){
14783         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14784     },
14785     
14786     /**
14787      * Case insensitive string
14788      * @param {Mixed} s The value being converted
14789      * @return {String} The comparison value
14790      */
14791     asUCString : function(s) {
14792         return String(s).toUpperCase();
14793     },
14794     
14795     /**
14796      * Date sorting
14797      * @param {Mixed} s The value being converted
14798      * @return {Number} The comparison value
14799      */
14800     asDate : function(s) {
14801         if(!s){
14802             return 0;
14803         }
14804         if(s instanceof Date){
14805             return s.getTime();
14806         }
14807         return Date.parse(String(s));
14808     },
14809     
14810     /**
14811      * Float sorting
14812      * @param {Mixed} s The value being converted
14813      * @return {Float} The comparison value
14814      */
14815     asFloat : function(s) {
14816         var val = parseFloat(String(s).replace(/,/g, ""));
14817         if(isNaN(val)) {
14818             val = 0;
14819         }
14820         return val;
14821     },
14822     
14823     /**
14824      * Integer sorting
14825      * @param {Mixed} s The value being converted
14826      * @return {Number} The comparison value
14827      */
14828     asInt : function(s) {
14829         var val = parseInt(String(s).replace(/,/g, ""));
14830         if(isNaN(val)) {
14831             val = 0;
14832         }
14833         return val;
14834     }
14835 };/*
14836  * Based on:
14837  * Ext JS Library 1.1.1
14838  * Copyright(c) 2006-2007, Ext JS, LLC.
14839  *
14840  * Originally Released Under LGPL - original licence link has changed is not relivant.
14841  *
14842  * Fork - LGPL
14843  * <script type="text/javascript">
14844  */
14845
14846 /**
14847 * @class Roo.data.Record
14848  * Instances of this class encapsulate both record <em>definition</em> information, and record
14849  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14850  * to access Records cached in an {@link Roo.data.Store} object.<br>
14851  * <p>
14852  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14853  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14854  * objects.<br>
14855  * <p>
14856  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14857  * @constructor
14858  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14859  * {@link #create}. The parameters are the same.
14860  * @param {Array} data An associative Array of data values keyed by the field name.
14861  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14862  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14863  * not specified an integer id is generated.
14864  */
14865 Roo.data.Record = function(data, id){
14866     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14867     this.data = data;
14868 };
14869
14870 /**
14871  * Generate a constructor for a specific record layout.
14872  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14873  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14874  * Each field definition object may contain the following properties: <ul>
14875  * <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,
14876  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14877  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14878  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14879  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14880  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14881  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14882  * this may be omitted.</p></li>
14883  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14884  * <ul><li>auto (Default, implies no conversion)</li>
14885  * <li>string</li>
14886  * <li>int</li>
14887  * <li>float</li>
14888  * <li>boolean</li>
14889  * <li>date</li></ul></p></li>
14890  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14891  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14892  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14893  * by the Reader into an object that will be stored in the Record. It is passed the
14894  * following parameters:<ul>
14895  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14896  * </ul></p></li>
14897  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14898  * </ul>
14899  * <br>usage:<br><pre><code>
14900 var TopicRecord = Roo.data.Record.create(
14901     {name: 'title', mapping: 'topic_title'},
14902     {name: 'author', mapping: 'username'},
14903     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14904     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14905     {name: 'lastPoster', mapping: 'user2'},
14906     {name: 'excerpt', mapping: 'post_text'}
14907 );
14908
14909 var myNewRecord = new TopicRecord({
14910     title: 'Do my job please',
14911     author: 'noobie',
14912     totalPosts: 1,
14913     lastPost: new Date(),
14914     lastPoster: 'Animal',
14915     excerpt: 'No way dude!'
14916 });
14917 myStore.add(myNewRecord);
14918 </code></pre>
14919  * @method create
14920  * @static
14921  */
14922 Roo.data.Record.create = function(o){
14923     var f = function(){
14924         f.superclass.constructor.apply(this, arguments);
14925     };
14926     Roo.extend(f, Roo.data.Record);
14927     var p = f.prototype;
14928     p.fields = new Roo.util.MixedCollection(false, function(field){
14929         return field.name;
14930     });
14931     for(var i = 0, len = o.length; i < len; i++){
14932         p.fields.add(new Roo.data.Field(o[i]));
14933     }
14934     f.getField = function(name){
14935         return p.fields.get(name);  
14936     };
14937     return f;
14938 };
14939
14940 Roo.data.Record.AUTO_ID = 1000;
14941 Roo.data.Record.EDIT = 'edit';
14942 Roo.data.Record.REJECT = 'reject';
14943 Roo.data.Record.COMMIT = 'commit';
14944
14945 Roo.data.Record.prototype = {
14946     /**
14947      * Readonly flag - true if this record has been modified.
14948      * @type Boolean
14949      */
14950     dirty : false,
14951     editing : false,
14952     error: null,
14953     modified: null,
14954
14955     // private
14956     join : function(store){
14957         this.store = store;
14958     },
14959
14960     /**
14961      * Set the named field to the specified value.
14962      * @param {String} name The name of the field to set.
14963      * @param {Object} value The value to set the field to.
14964      */
14965     set : function(name, value){
14966         if(this.data[name] == value){
14967             return;
14968         }
14969         this.dirty = true;
14970         if(!this.modified){
14971             this.modified = {};
14972         }
14973         if(typeof this.modified[name] == 'undefined'){
14974             this.modified[name] = this.data[name];
14975         }
14976         this.data[name] = value;
14977         if(!this.editing && this.store){
14978             this.store.afterEdit(this);
14979         }       
14980     },
14981
14982     /**
14983      * Get the value of the named field.
14984      * @param {String} name The name of the field to get the value of.
14985      * @return {Object} The value of the field.
14986      */
14987     get : function(name){
14988         return this.data[name]; 
14989     },
14990
14991     // private
14992     beginEdit : function(){
14993         this.editing = true;
14994         this.modified = {}; 
14995     },
14996
14997     // private
14998     cancelEdit : function(){
14999         this.editing = false;
15000         delete this.modified;
15001     },
15002
15003     // private
15004     endEdit : function(){
15005         this.editing = false;
15006         if(this.dirty && this.store){
15007             this.store.afterEdit(this);
15008         }
15009     },
15010
15011     /**
15012      * Usually called by the {@link Roo.data.Store} which owns the Record.
15013      * Rejects all changes made to the Record since either creation, or the last commit operation.
15014      * Modified fields are reverted to their original values.
15015      * <p>
15016      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15017      * of reject operations.
15018      */
15019     reject : function(){
15020         var m = this.modified;
15021         for(var n in m){
15022             if(typeof m[n] != "function"){
15023                 this.data[n] = m[n];
15024             }
15025         }
15026         this.dirty = false;
15027         delete this.modified;
15028         this.editing = false;
15029         if(this.store){
15030             this.store.afterReject(this);
15031         }
15032     },
15033
15034     /**
15035      * Usually called by the {@link Roo.data.Store} which owns the Record.
15036      * Commits all changes made to the Record since either creation, or the last commit operation.
15037      * <p>
15038      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15039      * of commit operations.
15040      */
15041     commit : function(){
15042         this.dirty = false;
15043         delete this.modified;
15044         this.editing = false;
15045         if(this.store){
15046             this.store.afterCommit(this);
15047         }
15048     },
15049
15050     // private
15051     hasError : function(){
15052         return this.error != null;
15053     },
15054
15055     // private
15056     clearError : function(){
15057         this.error = null;
15058     },
15059
15060     /**
15061      * Creates a copy of this record.
15062      * @param {String} id (optional) A new record id if you don't want to use this record's id
15063      * @return {Record}
15064      */
15065     copy : function(newId) {
15066         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15067     }
15068 };/*
15069  * Based on:
15070  * Ext JS Library 1.1.1
15071  * Copyright(c) 2006-2007, Ext JS, LLC.
15072  *
15073  * Originally Released Under LGPL - original licence link has changed is not relivant.
15074  *
15075  * Fork - LGPL
15076  * <script type="text/javascript">
15077  */
15078
15079
15080
15081 /**
15082  * @class Roo.data.Store
15083  * @extends Roo.util.Observable
15084  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15085  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15086  * <p>
15087  * 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
15088  * has no knowledge of the format of the data returned by the Proxy.<br>
15089  * <p>
15090  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15091  * instances from the data object. These records are cached and made available through accessor functions.
15092  * @constructor
15093  * Creates a new Store.
15094  * @param {Object} config A config object containing the objects needed for the Store to access data,
15095  * and read the data into Records.
15096  */
15097 Roo.data.Store = function(config){
15098     this.data = new Roo.util.MixedCollection(false);
15099     this.data.getKey = function(o){
15100         return o.id;
15101     };
15102     this.baseParams = {};
15103     // private
15104     this.paramNames = {
15105         "start" : "start",
15106         "limit" : "limit",
15107         "sort" : "sort",
15108         "dir" : "dir",
15109         "multisort" : "_multisort"
15110     };
15111
15112     if(config && config.data){
15113         this.inlineData = config.data;
15114         delete config.data;
15115     }
15116
15117     Roo.apply(this, config);
15118     
15119     if(this.reader){ // reader passed
15120         this.reader = Roo.factory(this.reader, Roo.data);
15121         this.reader.xmodule = this.xmodule || false;
15122         if(!this.recordType){
15123             this.recordType = this.reader.recordType;
15124         }
15125         if(this.reader.onMetaChange){
15126             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15127         }
15128     }
15129
15130     if(this.recordType){
15131         this.fields = this.recordType.prototype.fields;
15132     }
15133     this.modified = [];
15134
15135     this.addEvents({
15136         /**
15137          * @event datachanged
15138          * Fires when the data cache has changed, and a widget which is using this Store
15139          * as a Record cache should refresh its view.
15140          * @param {Store} this
15141          */
15142         datachanged : true,
15143         /**
15144          * @event metachange
15145          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15146          * @param {Store} this
15147          * @param {Object} meta The JSON metadata
15148          */
15149         metachange : true,
15150         /**
15151          * @event add
15152          * Fires when Records have been added to the Store
15153          * @param {Store} this
15154          * @param {Roo.data.Record[]} records The array of Records added
15155          * @param {Number} index The index at which the record(s) were added
15156          */
15157         add : true,
15158         /**
15159          * @event remove
15160          * Fires when a Record has been removed from the Store
15161          * @param {Store} this
15162          * @param {Roo.data.Record} record The Record that was removed
15163          * @param {Number} index The index at which the record was removed
15164          */
15165         remove : true,
15166         /**
15167          * @event update
15168          * Fires when a Record has been updated
15169          * @param {Store} this
15170          * @param {Roo.data.Record} record The Record that was updated
15171          * @param {String} operation The update operation being performed.  Value may be one of:
15172          * <pre><code>
15173  Roo.data.Record.EDIT
15174  Roo.data.Record.REJECT
15175  Roo.data.Record.COMMIT
15176          * </code></pre>
15177          */
15178         update : true,
15179         /**
15180          * @event clear
15181          * Fires when the data cache has been cleared.
15182          * @param {Store} this
15183          */
15184         clear : true,
15185         /**
15186          * @event beforeload
15187          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15188          * the load action will be canceled.
15189          * @param {Store} this
15190          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15191          */
15192         beforeload : true,
15193         /**
15194          * @event beforeloadadd
15195          * Fires after a new set of Records has been loaded.
15196          * @param {Store} this
15197          * @param {Roo.data.Record[]} records The Records that were loaded
15198          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15199          */
15200         beforeloadadd : true,
15201         /**
15202          * @event load
15203          * Fires after a new set of Records has been loaded, before they are added to the store.
15204          * @param {Store} this
15205          * @param {Roo.data.Record[]} records The Records that were loaded
15206          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15207          * @params {Object} return from reader
15208          */
15209         load : true,
15210         /**
15211          * @event loadexception
15212          * Fires if an exception occurs in the Proxy during loading.
15213          * Called with the signature of the Proxy's "loadexception" event.
15214          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15215          * 
15216          * @param {Proxy} 
15217          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15218          * @param {Object} load options 
15219          * @param {Object} jsonData from your request (normally this contains the Exception)
15220          */
15221         loadexception : true
15222     });
15223     
15224     if(this.proxy){
15225         this.proxy = Roo.factory(this.proxy, Roo.data);
15226         this.proxy.xmodule = this.xmodule || false;
15227         this.relayEvents(this.proxy,  ["loadexception"]);
15228     }
15229     this.sortToggle = {};
15230     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15231
15232     Roo.data.Store.superclass.constructor.call(this);
15233
15234     if(this.inlineData){
15235         this.loadData(this.inlineData);
15236         delete this.inlineData;
15237     }
15238 };
15239
15240 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15241      /**
15242     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15243     * without a remote query - used by combo/forms at present.
15244     */
15245     
15246     /**
15247     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15248     */
15249     /**
15250     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15251     */
15252     /**
15253     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15254     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15255     */
15256     /**
15257     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15258     * on any HTTP request
15259     */
15260     /**
15261     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15262     */
15263     /**
15264     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15265     */
15266     multiSort: false,
15267     /**
15268     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15269     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15270     */
15271     remoteSort : false,
15272
15273     /**
15274     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15275      * loaded or when a record is removed. (defaults to false).
15276     */
15277     pruneModifiedRecords : false,
15278
15279     // private
15280     lastOptions : null,
15281
15282     /**
15283      * Add Records to the Store and fires the add event.
15284      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15285      */
15286     add : function(records){
15287         records = [].concat(records);
15288         for(var i = 0, len = records.length; i < len; i++){
15289             records[i].join(this);
15290         }
15291         var index = this.data.length;
15292         this.data.addAll(records);
15293         this.fireEvent("add", this, records, index);
15294     },
15295
15296     /**
15297      * Remove a Record from the Store and fires the remove event.
15298      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15299      */
15300     remove : function(record){
15301         var index = this.data.indexOf(record);
15302         this.data.removeAt(index);
15303  
15304         if(this.pruneModifiedRecords){
15305             this.modified.remove(record);
15306         }
15307         this.fireEvent("remove", this, record, index);
15308     },
15309
15310     /**
15311      * Remove all Records from the Store and fires the clear event.
15312      */
15313     removeAll : function(){
15314         this.data.clear();
15315         if(this.pruneModifiedRecords){
15316             this.modified = [];
15317         }
15318         this.fireEvent("clear", this);
15319     },
15320
15321     /**
15322      * Inserts Records to the Store at the given index and fires the add event.
15323      * @param {Number} index The start index at which to insert the passed Records.
15324      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15325      */
15326     insert : function(index, records){
15327         records = [].concat(records);
15328         for(var i = 0, len = records.length; i < len; i++){
15329             this.data.insert(index, records[i]);
15330             records[i].join(this);
15331         }
15332         this.fireEvent("add", this, records, index);
15333     },
15334
15335     /**
15336      * Get the index within the cache of the passed Record.
15337      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15338      * @return {Number} The index of the passed Record. Returns -1 if not found.
15339      */
15340     indexOf : function(record){
15341         return this.data.indexOf(record);
15342     },
15343
15344     /**
15345      * Get the index within the cache of the Record with the passed id.
15346      * @param {String} id The id of the Record to find.
15347      * @return {Number} The index of the Record. Returns -1 if not found.
15348      */
15349     indexOfId : function(id){
15350         return this.data.indexOfKey(id);
15351     },
15352
15353     /**
15354      * Get the Record with the specified id.
15355      * @param {String} id The id of the Record to find.
15356      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15357      */
15358     getById : function(id){
15359         return this.data.key(id);
15360     },
15361
15362     /**
15363      * Get the Record at the specified index.
15364      * @param {Number} index The index of the Record to find.
15365      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15366      */
15367     getAt : function(index){
15368         return this.data.itemAt(index);
15369     },
15370
15371     /**
15372      * Returns a range of Records between specified indices.
15373      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15374      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15375      * @return {Roo.data.Record[]} An array of Records
15376      */
15377     getRange : function(start, end){
15378         return this.data.getRange(start, end);
15379     },
15380
15381     // private
15382     storeOptions : function(o){
15383         o = Roo.apply({}, o);
15384         delete o.callback;
15385         delete o.scope;
15386         this.lastOptions = o;
15387     },
15388
15389     /**
15390      * Loads the Record cache from the configured Proxy using the configured Reader.
15391      * <p>
15392      * If using remote paging, then the first load call must specify the <em>start</em>
15393      * and <em>limit</em> properties in the options.params property to establish the initial
15394      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15395      * <p>
15396      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15397      * and this call will return before the new data has been loaded. Perform any post-processing
15398      * in a callback function, or in a "load" event handler.</strong>
15399      * <p>
15400      * @param {Object} options An object containing properties which control loading options:<ul>
15401      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15402      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15403      * <pre>
15404                 {
15405                     data : data,  // array of key=>value data like JsonReader
15406                     total : data.length,
15407                     success : true
15408                     
15409                 }
15410         </pre>
15411             }.</li>
15412      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15413      * passed the following arguments:<ul>
15414      * <li>r : Roo.data.Record[]</li>
15415      * <li>options: Options object from the load call</li>
15416      * <li>success: Boolean success indicator</li></ul></li>
15417      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15418      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15419      * </ul>
15420      */
15421     load : function(options){
15422         options = options || {};
15423         if(this.fireEvent("beforeload", this, options) !== false){
15424             this.storeOptions(options);
15425             var p = Roo.apply(options.params || {}, this.baseParams);
15426             // if meta was not loaded from remote source.. try requesting it.
15427             if (!this.reader.metaFromRemote) {
15428                 p._requestMeta = 1;
15429             }
15430             if(this.sortInfo && this.remoteSort){
15431                 var pn = this.paramNames;
15432                 p[pn["sort"]] = this.sortInfo.field;
15433                 p[pn["dir"]] = this.sortInfo.direction;
15434             }
15435             if (this.multiSort) {
15436                 var pn = this.paramNames;
15437                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15438             }
15439             
15440             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15441         }
15442     },
15443
15444     /**
15445      * Reloads the Record cache from the configured Proxy using the configured Reader and
15446      * the options from the last load operation performed.
15447      * @param {Object} options (optional) An object containing properties which may override the options
15448      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15449      * the most recently used options are reused).
15450      */
15451     reload : function(options){
15452         this.load(Roo.applyIf(options||{}, this.lastOptions));
15453     },
15454
15455     // private
15456     // Called as a callback by the Reader during a load operation.
15457     loadRecords : function(o, options, success){
15458          
15459         if(!o){
15460             if(success !== false){
15461                 this.fireEvent("load", this, [], options, o);
15462             }
15463             if(options.callback){
15464                 options.callback.call(options.scope || this, [], options, false);
15465             }
15466             return;
15467         }
15468         // if data returned failure - throw an exception.
15469         if (o.success === false) {
15470             // show a message if no listener is registered.
15471             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15472                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15473             }
15474             // loadmask wil be hooked into this..
15475             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15476             return;
15477         }
15478         var r = o.records, t = o.totalRecords || r.length;
15479         
15480         this.fireEvent("beforeloadadd", this, r, options, o);
15481         
15482         if(!options || options.add !== true){
15483             if(this.pruneModifiedRecords){
15484                 this.modified = [];
15485             }
15486             for(var i = 0, len = r.length; i < len; i++){
15487                 r[i].join(this);
15488             }
15489             if(this.snapshot){
15490                 this.data = this.snapshot;
15491                 delete this.snapshot;
15492             }
15493             this.data.clear();
15494             this.data.addAll(r);
15495             this.totalLength = t;
15496             this.applySort();
15497             this.fireEvent("datachanged", this);
15498         }else{
15499             this.totalLength = Math.max(t, this.data.length+r.length);
15500             this.add(r);
15501         }
15502         
15503         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15504                 
15505             var e = new Roo.data.Record({});
15506
15507             e.set(this.parent.displayField, this.parent.emptyTitle);
15508             e.set(this.parent.valueField, '');
15509
15510             this.insert(0, e);
15511         }
15512             
15513         this.fireEvent("load", this, r, options, o);
15514         if(options.callback){
15515             options.callback.call(options.scope || this, r, options, true);
15516         }
15517     },
15518
15519
15520     /**
15521      * Loads data from a passed data block. A Reader which understands the format of the data
15522      * must have been configured in the constructor.
15523      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15524      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15525      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15526      */
15527     loadData : function(o, append){
15528         var r = this.reader.readRecords(o);
15529         this.loadRecords(r, {add: append}, true);
15530     },
15531     
15532      /**
15533      * using 'cn' the nested child reader read the child array into it's child stores.
15534      * @param {Object} rec The record with a 'children array
15535      */
15536     loadDataFromChildren : function(rec)
15537     {
15538         this.loadData(this.reader.toLoadData(rec));
15539     },
15540     
15541
15542     /**
15543      * Gets the number of cached records.
15544      * <p>
15545      * <em>If using paging, this may not be the total size of the dataset. If the data object
15546      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15547      * the data set size</em>
15548      */
15549     getCount : function(){
15550         return this.data.length || 0;
15551     },
15552
15553     /**
15554      * Gets the total number of records in the dataset as returned by the server.
15555      * <p>
15556      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15557      * the dataset size</em>
15558      */
15559     getTotalCount : function(){
15560         return this.totalLength || 0;
15561     },
15562
15563     /**
15564      * Returns the sort state of the Store as an object with two properties:
15565      * <pre><code>
15566  field {String} The name of the field by which the Records are sorted
15567  direction {String} The sort order, "ASC" or "DESC"
15568      * </code></pre>
15569      */
15570     getSortState : function(){
15571         return this.sortInfo;
15572     },
15573
15574     // private
15575     applySort : function(){
15576         if(this.sortInfo && !this.remoteSort){
15577             var s = this.sortInfo, f = s.field;
15578             var st = this.fields.get(f).sortType;
15579             var fn = function(r1, r2){
15580                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15581                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15582             };
15583             this.data.sort(s.direction, fn);
15584             if(this.snapshot && this.snapshot != this.data){
15585                 this.snapshot.sort(s.direction, fn);
15586             }
15587         }
15588     },
15589
15590     /**
15591      * Sets the default sort column and order to be used by the next load operation.
15592      * @param {String} fieldName The name of the field to sort by.
15593      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15594      */
15595     setDefaultSort : function(field, dir){
15596         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15597     },
15598
15599     /**
15600      * Sort the Records.
15601      * If remote sorting is used, the sort is performed on the server, and the cache is
15602      * reloaded. If local sorting is used, the cache is sorted internally.
15603      * @param {String} fieldName The name of the field to sort by.
15604      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15605      */
15606     sort : function(fieldName, dir){
15607         var f = this.fields.get(fieldName);
15608         if(!dir){
15609             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15610             
15611             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15612                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15613             }else{
15614                 dir = f.sortDir;
15615             }
15616         }
15617         this.sortToggle[f.name] = dir;
15618         this.sortInfo = {field: f.name, direction: dir};
15619         if(!this.remoteSort){
15620             this.applySort();
15621             this.fireEvent("datachanged", this);
15622         }else{
15623             this.load(this.lastOptions);
15624         }
15625     },
15626
15627     /**
15628      * Calls the specified function for each of the Records in the cache.
15629      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15630      * Returning <em>false</em> aborts and exits the iteration.
15631      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15632      */
15633     each : function(fn, scope){
15634         this.data.each(fn, scope);
15635     },
15636
15637     /**
15638      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15639      * (e.g., during paging).
15640      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15641      */
15642     getModifiedRecords : function(){
15643         return this.modified;
15644     },
15645
15646     // private
15647     createFilterFn : function(property, value, anyMatch){
15648         if(!value.exec){ // not a regex
15649             value = String(value);
15650             if(value.length == 0){
15651                 return false;
15652             }
15653             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15654         }
15655         return function(r){
15656             return value.test(r.data[property]);
15657         };
15658     },
15659
15660     /**
15661      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15662      * @param {String} property A field on your records
15663      * @param {Number} start The record index to start at (defaults to 0)
15664      * @param {Number} end The last record index to include (defaults to length - 1)
15665      * @return {Number} The sum
15666      */
15667     sum : function(property, start, end){
15668         var rs = this.data.items, v = 0;
15669         start = start || 0;
15670         end = (end || end === 0) ? end : rs.length-1;
15671
15672         for(var i = start; i <= end; i++){
15673             v += (rs[i].data[property] || 0);
15674         }
15675         return v;
15676     },
15677
15678     /**
15679      * Filter the records by a specified property.
15680      * @param {String} field A field on your records
15681      * @param {String/RegExp} value Either a string that the field
15682      * should start with or a RegExp to test against the field
15683      * @param {Boolean} anyMatch True to match any part not just the beginning
15684      */
15685     filter : function(property, value, anyMatch){
15686         var fn = this.createFilterFn(property, value, anyMatch);
15687         return fn ? this.filterBy(fn) : this.clearFilter();
15688     },
15689
15690     /**
15691      * Filter by a function. The specified function will be called with each
15692      * record in this data source. If the function returns true the record is included,
15693      * otherwise it is filtered.
15694      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15695      * @param {Object} scope (optional) The scope of the function (defaults to this)
15696      */
15697     filterBy : function(fn, scope){
15698         this.snapshot = this.snapshot || this.data;
15699         this.data = this.queryBy(fn, scope||this);
15700         this.fireEvent("datachanged", this);
15701     },
15702
15703     /**
15704      * Query the records by a specified property.
15705      * @param {String} field A field on your records
15706      * @param {String/RegExp} value Either a string that the field
15707      * should start with or a RegExp to test against the field
15708      * @param {Boolean} anyMatch True to match any part not just the beginning
15709      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15710      */
15711     query : function(property, value, anyMatch){
15712         var fn = this.createFilterFn(property, value, anyMatch);
15713         return fn ? this.queryBy(fn) : this.data.clone();
15714     },
15715
15716     /**
15717      * Query by a function. The specified function will be called with each
15718      * record in this data source. If the function returns true the record is included
15719      * in the results.
15720      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15721      * @param {Object} scope (optional) The scope of the function (defaults to this)
15722       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15723      **/
15724     queryBy : function(fn, scope){
15725         var data = this.snapshot || this.data;
15726         return data.filterBy(fn, scope||this);
15727     },
15728
15729     /**
15730      * Collects unique values for a particular dataIndex from this store.
15731      * @param {String} dataIndex The property to collect
15732      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15733      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15734      * @return {Array} An array of the unique values
15735      **/
15736     collect : function(dataIndex, allowNull, bypassFilter){
15737         var d = (bypassFilter === true && this.snapshot) ?
15738                 this.snapshot.items : this.data.items;
15739         var v, sv, r = [], l = {};
15740         for(var i = 0, len = d.length; i < len; i++){
15741             v = d[i].data[dataIndex];
15742             sv = String(v);
15743             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15744                 l[sv] = true;
15745                 r[r.length] = v;
15746             }
15747         }
15748         return r;
15749     },
15750
15751     /**
15752      * Revert to a view of the Record cache with no filtering applied.
15753      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15754      */
15755     clearFilter : function(suppressEvent){
15756         if(this.snapshot && this.snapshot != this.data){
15757             this.data = this.snapshot;
15758             delete this.snapshot;
15759             if(suppressEvent !== true){
15760                 this.fireEvent("datachanged", this);
15761             }
15762         }
15763     },
15764
15765     // private
15766     afterEdit : function(record){
15767         if(this.modified.indexOf(record) == -1){
15768             this.modified.push(record);
15769         }
15770         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15771     },
15772     
15773     // private
15774     afterReject : function(record){
15775         this.modified.remove(record);
15776         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15777     },
15778
15779     // private
15780     afterCommit : function(record){
15781         this.modified.remove(record);
15782         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15783     },
15784
15785     /**
15786      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15787      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15788      */
15789     commitChanges : function(){
15790         var m = this.modified.slice(0);
15791         this.modified = [];
15792         for(var i = 0, len = m.length; i < len; i++){
15793             m[i].commit();
15794         }
15795     },
15796
15797     /**
15798      * Cancel outstanding changes on all changed records.
15799      */
15800     rejectChanges : function(){
15801         var m = this.modified.slice(0);
15802         this.modified = [];
15803         for(var i = 0, len = m.length; i < len; i++){
15804             m[i].reject();
15805         }
15806     },
15807
15808     onMetaChange : function(meta, rtype, o){
15809         this.recordType = rtype;
15810         this.fields = rtype.prototype.fields;
15811         delete this.snapshot;
15812         this.sortInfo = meta.sortInfo || this.sortInfo;
15813         this.modified = [];
15814         this.fireEvent('metachange', this, this.reader.meta);
15815     },
15816     
15817     moveIndex : function(data, type)
15818     {
15819         var index = this.indexOf(data);
15820         
15821         var newIndex = index + type;
15822         
15823         this.remove(data);
15824         
15825         this.insert(newIndex, data);
15826         
15827     }
15828 });/*
15829  * Based on:
15830  * Ext JS Library 1.1.1
15831  * Copyright(c) 2006-2007, Ext JS, LLC.
15832  *
15833  * Originally Released Under LGPL - original licence link has changed is not relivant.
15834  *
15835  * Fork - LGPL
15836  * <script type="text/javascript">
15837  */
15838
15839 /**
15840  * @class Roo.data.SimpleStore
15841  * @extends Roo.data.Store
15842  * Small helper class to make creating Stores from Array data easier.
15843  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15844  * @cfg {Array} fields An array of field definition objects, or field name strings.
15845  * @cfg {Object} an existing reader (eg. copied from another store)
15846  * @cfg {Array} data The multi-dimensional array of data
15847  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15848  * @cfg {Roo.data.Reader} reader  [not-required] 
15849  * @constructor
15850  * @param {Object} config
15851  */
15852 Roo.data.SimpleStore = function(config)
15853 {
15854     Roo.data.SimpleStore.superclass.constructor.call(this, {
15855         isLocal : true,
15856         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15857                 id: config.id
15858             },
15859             Roo.data.Record.create(config.fields)
15860         ),
15861         proxy : new Roo.data.MemoryProxy(config.data)
15862     });
15863     this.load();
15864 };
15865 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15866  * Based on:
15867  * Ext JS Library 1.1.1
15868  * Copyright(c) 2006-2007, Ext JS, LLC.
15869  *
15870  * Originally Released Under LGPL - original licence link has changed is not relivant.
15871  *
15872  * Fork - LGPL
15873  * <script type="text/javascript">
15874  */
15875
15876 /**
15877 /**
15878  * @extends Roo.data.Store
15879  * @class Roo.data.JsonStore
15880  * Small helper class to make creating Stores for JSON data easier. <br/>
15881 <pre><code>
15882 var store = new Roo.data.JsonStore({
15883     url: 'get-images.php',
15884     root: 'images',
15885     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15886 });
15887 </code></pre>
15888  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15889  * JsonReader and HttpProxy (unless inline data is provided).</b>
15890  * @cfg {Array} fields An array of field definition objects, or field name strings.
15891  * @constructor
15892  * @param {Object} config
15893  */
15894 Roo.data.JsonStore = function(c){
15895     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15896         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15897         reader: new Roo.data.JsonReader(c, c.fields)
15898     }));
15899 };
15900 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15901  * Based on:
15902  * Ext JS Library 1.1.1
15903  * Copyright(c) 2006-2007, Ext JS, LLC.
15904  *
15905  * Originally Released Under LGPL - original licence link has changed is not relivant.
15906  *
15907  * Fork - LGPL
15908  * <script type="text/javascript">
15909  */
15910
15911  
15912 Roo.data.Field = function(config){
15913     if(typeof config == "string"){
15914         config = {name: config};
15915     }
15916     Roo.apply(this, config);
15917     
15918     if(!this.type){
15919         this.type = "auto";
15920     }
15921     
15922     var st = Roo.data.SortTypes;
15923     // named sortTypes are supported, here we look them up
15924     if(typeof this.sortType == "string"){
15925         this.sortType = st[this.sortType];
15926     }
15927     
15928     // set default sortType for strings and dates
15929     if(!this.sortType){
15930         switch(this.type){
15931             case "string":
15932                 this.sortType = st.asUCString;
15933                 break;
15934             case "date":
15935                 this.sortType = st.asDate;
15936                 break;
15937             default:
15938                 this.sortType = st.none;
15939         }
15940     }
15941
15942     // define once
15943     var stripRe = /[\$,%]/g;
15944
15945     // prebuilt conversion function for this field, instead of
15946     // switching every time we're reading a value
15947     if(!this.convert){
15948         var cv, dateFormat = this.dateFormat;
15949         switch(this.type){
15950             case "":
15951             case "auto":
15952             case undefined:
15953                 cv = function(v){ return v; };
15954                 break;
15955             case "string":
15956                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15957                 break;
15958             case "int":
15959                 cv = function(v){
15960                     return v !== undefined && v !== null && v !== '' ?
15961                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15962                     };
15963                 break;
15964             case "float":
15965                 cv = function(v){
15966                     return v !== undefined && v !== null && v !== '' ?
15967                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15968                     };
15969                 break;
15970             case "bool":
15971             case "boolean":
15972                 cv = function(v){ return v === true || v === "true" || v == 1; };
15973                 break;
15974             case "date":
15975                 cv = function(v){
15976                     if(!v){
15977                         return '';
15978                     }
15979                     if(v instanceof Date){
15980                         return v;
15981                     }
15982                     if(dateFormat){
15983                         if(dateFormat == "timestamp"){
15984                             return new Date(v*1000);
15985                         }
15986                         return Date.parseDate(v, dateFormat);
15987                     }
15988                     var parsed = Date.parse(v);
15989                     return parsed ? new Date(parsed) : null;
15990                 };
15991              break;
15992             
15993         }
15994         this.convert = cv;
15995     }
15996 };
15997
15998 Roo.data.Field.prototype = {
15999     dateFormat: null,
16000     defaultValue: "",
16001     mapping: null,
16002     sortType : null,
16003     sortDir : "ASC"
16004 };/*
16005  * Based on:
16006  * Ext JS Library 1.1.1
16007  * Copyright(c) 2006-2007, Ext JS, LLC.
16008  *
16009  * Originally Released Under LGPL - original licence link has changed is not relivant.
16010  *
16011  * Fork - LGPL
16012  * <script type="text/javascript">
16013  */
16014  
16015 // Base class for reading structured data from a data source.  This class is intended to be
16016 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
16017
16018 /**
16019  * @class Roo.data.DataReader
16020  * @abstract
16021  * Base class for reading structured data from a data source.  This class is intended to be
16022  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
16023  */
16024
16025 Roo.data.DataReader = function(meta, recordType){
16026     
16027     this.meta = meta;
16028     
16029     this.recordType = recordType instanceof Array ? 
16030         Roo.data.Record.create(recordType) : recordType;
16031 };
16032
16033 Roo.data.DataReader.prototype = {
16034     
16035     
16036     readerType : 'Data',
16037      /**
16038      * Create an empty record
16039      * @param {Object} data (optional) - overlay some values
16040      * @return {Roo.data.Record} record created.
16041      */
16042     newRow :  function(d) {
16043         var da =  {};
16044         this.recordType.prototype.fields.each(function(c) {
16045             switch( c.type) {
16046                 case 'int' : da[c.name] = 0; break;
16047                 case 'date' : da[c.name] = new Date(); break;
16048                 case 'float' : da[c.name] = 0.0; break;
16049                 case 'boolean' : da[c.name] = false; break;
16050                 default : da[c.name] = ""; break;
16051             }
16052             
16053         });
16054         return new this.recordType(Roo.apply(da, d));
16055     }
16056     
16057     
16058 };/*
16059  * Based on:
16060  * Ext JS Library 1.1.1
16061  * Copyright(c) 2006-2007, Ext JS, LLC.
16062  *
16063  * Originally Released Under LGPL - original licence link has changed is not relivant.
16064  *
16065  * Fork - LGPL
16066  * <script type="text/javascript">
16067  */
16068
16069 /**
16070  * @class Roo.data.DataProxy
16071  * @extends Roo.util.Observable
16072  * @abstract
16073  * This class is an abstract base class for implementations which provide retrieval of
16074  * unformatted data objects.<br>
16075  * <p>
16076  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16077  * (of the appropriate type which knows how to parse the data object) to provide a block of
16078  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16079  * <p>
16080  * Custom implementations must implement the load method as described in
16081  * {@link Roo.data.HttpProxy#load}.
16082  */
16083 Roo.data.DataProxy = function(){
16084     this.addEvents({
16085         /**
16086          * @event beforeload
16087          * Fires before a network request is made to retrieve a data object.
16088          * @param {Object} This DataProxy object.
16089          * @param {Object} params The params parameter to the load function.
16090          */
16091         beforeload : true,
16092         /**
16093          * @event load
16094          * Fires before the load method's callback is called.
16095          * @param {Object} This DataProxy object.
16096          * @param {Object} o The data object.
16097          * @param {Object} arg The callback argument object passed to the load function.
16098          */
16099         load : true,
16100         /**
16101          * @event loadexception
16102          * Fires if an Exception occurs during data retrieval.
16103          * @param {Object} This DataProxy object.
16104          * @param {Object} o The data object.
16105          * @param {Object} arg The callback argument object passed to the load function.
16106          * @param {Object} e The Exception.
16107          */
16108         loadexception : true
16109     });
16110     Roo.data.DataProxy.superclass.constructor.call(this);
16111 };
16112
16113 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16114
16115     /**
16116      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16117      */
16118 /*
16119  * Based on:
16120  * Ext JS Library 1.1.1
16121  * Copyright(c) 2006-2007, Ext JS, LLC.
16122  *
16123  * Originally Released Under LGPL - original licence link has changed is not relivant.
16124  *
16125  * Fork - LGPL
16126  * <script type="text/javascript">
16127  */
16128 /**
16129  * @class Roo.data.MemoryProxy
16130  * @extends Roo.data.DataProxy
16131  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16132  * to the Reader when its load method is called.
16133  * @constructor
16134  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16135  */
16136 Roo.data.MemoryProxy = function(config){
16137     var data = config;
16138     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16139         data = config.data;
16140     }
16141     Roo.data.MemoryProxy.superclass.constructor.call(this);
16142     this.data = data;
16143 };
16144
16145 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16146     
16147     /**
16148      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16149      */
16150     /**
16151      * Load data from the requested source (in this case an in-memory
16152      * data object passed to the constructor), read the data object into
16153      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16154      * process that block using the passed callback.
16155      * @param {Object} params This parameter is not used by the MemoryProxy class.
16156      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16157      * object into a block of Roo.data.Records.
16158      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16159      * The function must be passed <ul>
16160      * <li>The Record block object</li>
16161      * <li>The "arg" argument from the load function</li>
16162      * <li>A boolean success indicator</li>
16163      * </ul>
16164      * @param {Object} scope The scope in which to call the callback
16165      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16166      */
16167     load : function(params, reader, callback, scope, arg){
16168         params = params || {};
16169         var result;
16170         try {
16171             result = reader.readRecords(params.data ? params.data :this.data);
16172         }catch(e){
16173             this.fireEvent("loadexception", this, arg, null, e);
16174             callback.call(scope, null, arg, false);
16175             return;
16176         }
16177         callback.call(scope, result, arg, true);
16178     },
16179     
16180     // private
16181     update : function(params, records){
16182         
16183     }
16184 });/*
16185  * Based on:
16186  * Ext JS Library 1.1.1
16187  * Copyright(c) 2006-2007, Ext JS, LLC.
16188  *
16189  * Originally Released Under LGPL - original licence link has changed is not relivant.
16190  *
16191  * Fork - LGPL
16192  * <script type="text/javascript">
16193  */
16194 /**
16195  * @class Roo.data.HttpProxy
16196  * @extends Roo.data.DataProxy
16197  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16198  * configured to reference a certain URL.<br><br>
16199  * <p>
16200  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16201  * from which the running page was served.<br><br>
16202  * <p>
16203  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16204  * <p>
16205  * Be aware that to enable the browser to parse an XML document, the server must set
16206  * the Content-Type header in the HTTP response to "text/xml".
16207  * @constructor
16208  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16209  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16210  * will be used to make the request.
16211  */
16212 Roo.data.HttpProxy = function(conn){
16213     Roo.data.HttpProxy.superclass.constructor.call(this);
16214     // is conn a conn config or a real conn?
16215     this.conn = conn;
16216     this.useAjax = !conn || !conn.events;
16217   
16218 };
16219
16220 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16221     // thse are take from connection...
16222     
16223     /**
16224      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16225      */
16226     /**
16227      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16228      * extra parameters to each request made by this object. (defaults to undefined)
16229      */
16230     /**
16231      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16232      *  to each request made by this object. (defaults to undefined)
16233      */
16234     /**
16235      * @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)
16236      */
16237     /**
16238      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16239      */
16240      /**
16241      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16242      * @type Boolean
16243      */
16244   
16245
16246     /**
16247      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16248      * @type Boolean
16249      */
16250     /**
16251      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16252      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16253      * a finer-grained basis than the DataProxy events.
16254      */
16255     getConnection : function(){
16256         return this.useAjax ? Roo.Ajax : this.conn;
16257     },
16258
16259     /**
16260      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16261      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16262      * process that block using the passed callback.
16263      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16264      * for the request to the remote server.
16265      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16266      * object into a block of Roo.data.Records.
16267      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16268      * The function must be passed <ul>
16269      * <li>The Record block object</li>
16270      * <li>The "arg" argument from the load function</li>
16271      * <li>A boolean success indicator</li>
16272      * </ul>
16273      * @param {Object} scope The scope in which to call the callback
16274      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16275      */
16276     load : function(params, reader, callback, scope, arg){
16277         if(this.fireEvent("beforeload", this, params) !== false){
16278             var  o = {
16279                 params : params || {},
16280                 request: {
16281                     callback : callback,
16282                     scope : scope,
16283                     arg : arg
16284                 },
16285                 reader: reader,
16286                 callback : this.loadResponse,
16287                 scope: this
16288             };
16289             if(this.useAjax){
16290                 Roo.applyIf(o, this.conn);
16291                 if(this.activeRequest){
16292                     Roo.Ajax.abort(this.activeRequest);
16293                 }
16294                 this.activeRequest = Roo.Ajax.request(o);
16295             }else{
16296                 this.conn.request(o);
16297             }
16298         }else{
16299             callback.call(scope||this, null, arg, false);
16300         }
16301     },
16302
16303     // private
16304     loadResponse : function(o, success, response){
16305         delete this.activeRequest;
16306         if(!success){
16307             this.fireEvent("loadexception", this, o, response);
16308             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16309             return;
16310         }
16311         var result;
16312         try {
16313             result = o.reader.read(response);
16314         }catch(e){
16315             o.success = false;
16316             o.raw = { errorMsg : response.responseText };
16317             this.fireEvent("loadexception", this, o, response, e);
16318             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16319             return;
16320         }
16321         
16322         this.fireEvent("load", this, o, o.request.arg);
16323         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16324     },
16325
16326     // private
16327     update : function(dataSet){
16328
16329     },
16330
16331     // private
16332     updateResponse : function(dataSet){
16333
16334     }
16335 });/*
16336  * Based on:
16337  * Ext JS Library 1.1.1
16338  * Copyright(c) 2006-2007, Ext JS, LLC.
16339  *
16340  * Originally Released Under LGPL - original licence link has changed is not relivant.
16341  *
16342  * Fork - LGPL
16343  * <script type="text/javascript">
16344  */
16345
16346 /**
16347  * @class Roo.data.ScriptTagProxy
16348  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16349  * other than the originating domain of the running page.<br><br>
16350  * <p>
16351  * <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
16352  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16353  * <p>
16354  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16355  * source code that is used as the source inside a &lt;script> tag.<br><br>
16356  * <p>
16357  * In order for the browser to process the returned data, the server must wrap the data object
16358  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16359  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16360  * depending on whether the callback name was passed:
16361  * <p>
16362  * <pre><code>
16363 boolean scriptTag = false;
16364 String cb = request.getParameter("callback");
16365 if (cb != null) {
16366     scriptTag = true;
16367     response.setContentType("text/javascript");
16368 } else {
16369     response.setContentType("application/x-json");
16370 }
16371 Writer out = response.getWriter();
16372 if (scriptTag) {
16373     out.write(cb + "(");
16374 }
16375 out.print(dataBlock.toJsonString());
16376 if (scriptTag) {
16377     out.write(");");
16378 }
16379 </pre></code>
16380  *
16381  * @constructor
16382  * @param {Object} config A configuration object.
16383  */
16384 Roo.data.ScriptTagProxy = function(config){
16385     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16386     Roo.apply(this, config);
16387     this.head = document.getElementsByTagName("head")[0];
16388 };
16389
16390 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16391
16392 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16393     /**
16394      * @cfg {String} url The URL from which to request the data object.
16395      */
16396     /**
16397      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16398      */
16399     timeout : 30000,
16400     /**
16401      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16402      * the server the name of the callback function set up by the load call to process the returned data object.
16403      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16404      * javascript output which calls this named function passing the data object as its only parameter.
16405      */
16406     callbackParam : "callback",
16407     /**
16408      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16409      * name to the request.
16410      */
16411     nocache : true,
16412
16413     /**
16414      * Load data from the configured URL, read the data object into
16415      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16416      * process that block using the passed callback.
16417      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16418      * for the request to the remote server.
16419      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16420      * object into a block of Roo.data.Records.
16421      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16422      * The function must be passed <ul>
16423      * <li>The Record block object</li>
16424      * <li>The "arg" argument from the load function</li>
16425      * <li>A boolean success indicator</li>
16426      * </ul>
16427      * @param {Object} scope The scope in which to call the callback
16428      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16429      */
16430     load : function(params, reader, callback, scope, arg){
16431         if(this.fireEvent("beforeload", this, params) !== false){
16432
16433             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16434
16435             var url = this.url;
16436             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16437             if(this.nocache){
16438                 url += "&_dc=" + (new Date().getTime());
16439             }
16440             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16441             var trans = {
16442                 id : transId,
16443                 cb : "stcCallback"+transId,
16444                 scriptId : "stcScript"+transId,
16445                 params : params,
16446                 arg : arg,
16447                 url : url,
16448                 callback : callback,
16449                 scope : scope,
16450                 reader : reader
16451             };
16452             var conn = this;
16453
16454             window[trans.cb] = function(o){
16455                 conn.handleResponse(o, trans);
16456             };
16457
16458             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16459
16460             if(this.autoAbort !== false){
16461                 this.abort();
16462             }
16463
16464             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16465
16466             var script = document.createElement("script");
16467             script.setAttribute("src", url);
16468             script.setAttribute("type", "text/javascript");
16469             script.setAttribute("id", trans.scriptId);
16470             this.head.appendChild(script);
16471
16472             this.trans = trans;
16473         }else{
16474             callback.call(scope||this, null, arg, false);
16475         }
16476     },
16477
16478     // private
16479     isLoading : function(){
16480         return this.trans ? true : false;
16481     },
16482
16483     /**
16484      * Abort the current server request.
16485      */
16486     abort : function(){
16487         if(this.isLoading()){
16488             this.destroyTrans(this.trans);
16489         }
16490     },
16491
16492     // private
16493     destroyTrans : function(trans, isLoaded){
16494         this.head.removeChild(document.getElementById(trans.scriptId));
16495         clearTimeout(trans.timeoutId);
16496         if(isLoaded){
16497             window[trans.cb] = undefined;
16498             try{
16499                 delete window[trans.cb];
16500             }catch(e){}
16501         }else{
16502             // if hasn't been loaded, wait for load to remove it to prevent script error
16503             window[trans.cb] = function(){
16504                 window[trans.cb] = undefined;
16505                 try{
16506                     delete window[trans.cb];
16507                 }catch(e){}
16508             };
16509         }
16510     },
16511
16512     // private
16513     handleResponse : function(o, trans){
16514         this.trans = false;
16515         this.destroyTrans(trans, true);
16516         var result;
16517         try {
16518             result = trans.reader.readRecords(o);
16519         }catch(e){
16520             this.fireEvent("loadexception", this, o, trans.arg, e);
16521             trans.callback.call(trans.scope||window, null, trans.arg, false);
16522             return;
16523         }
16524         this.fireEvent("load", this, o, trans.arg);
16525         trans.callback.call(trans.scope||window, result, trans.arg, true);
16526     },
16527
16528     // private
16529     handleFailure : function(trans){
16530         this.trans = false;
16531         this.destroyTrans(trans, false);
16532         this.fireEvent("loadexception", this, null, trans.arg);
16533         trans.callback.call(trans.scope||window, null, trans.arg, false);
16534     }
16535 });/*
16536  * Based on:
16537  * Ext JS Library 1.1.1
16538  * Copyright(c) 2006-2007, Ext JS, LLC.
16539  *
16540  * Originally Released Under LGPL - original licence link has changed is not relivant.
16541  *
16542  * Fork - LGPL
16543  * <script type="text/javascript">
16544  */
16545
16546 /**
16547  * @class Roo.data.JsonReader
16548  * @extends Roo.data.DataReader
16549  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16550  * based on mappings in a provided Roo.data.Record constructor.
16551  * 
16552  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16553  * in the reply previously. 
16554  * 
16555  * <p>
16556  * Example code:
16557  * <pre><code>
16558 var RecordDef = Roo.data.Record.create([
16559     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16560     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16561 ]);
16562 var myReader = new Roo.data.JsonReader({
16563     totalProperty: "results",    // The property which contains the total dataset size (optional)
16564     root: "rows",                // The property which contains an Array of row objects
16565     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16566 }, RecordDef);
16567 </code></pre>
16568  * <p>
16569  * This would consume a JSON file like this:
16570  * <pre><code>
16571 { 'results': 2, 'rows': [
16572     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16573     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16574 }
16575 </code></pre>
16576  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16577  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16578  * paged from the remote server.
16579  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16580  * @cfg {String} root name of the property which contains the Array of row objects.
16581  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16582  * @cfg {Array} fields Array of field definition objects
16583  * @constructor
16584  * Create a new JsonReader
16585  * @param {Object} meta Metadata configuration options
16586  * @param {Object} recordType Either an Array of field definition objects,
16587  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16588  */
16589 Roo.data.JsonReader = function(meta, recordType){
16590     
16591     meta = meta || {};
16592     // set some defaults:
16593     Roo.applyIf(meta, {
16594         totalProperty: 'total',
16595         successProperty : 'success',
16596         root : 'data',
16597         id : 'id'
16598     });
16599     
16600     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16601 };
16602 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16603     
16604     readerType : 'Json',
16605     
16606     /**
16607      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16608      * Used by Store query builder to append _requestMeta to params.
16609      * 
16610      */
16611     metaFromRemote : false,
16612     /**
16613      * This method is only used by a DataProxy which has retrieved data from a remote server.
16614      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16615      * @return {Object} data A data block which is used by an Roo.data.Store object as
16616      * a cache of Roo.data.Records.
16617      */
16618     read : function(response){
16619         var json = response.responseText;
16620        
16621         var o = /* eval:var:o */ eval("("+json+")");
16622         if(!o) {
16623             throw {message: "JsonReader.read: Json object not found"};
16624         }
16625         
16626         if(o.metaData){
16627             
16628             delete this.ef;
16629             this.metaFromRemote = true;
16630             this.meta = o.metaData;
16631             this.recordType = Roo.data.Record.create(o.metaData.fields);
16632             this.onMetaChange(this.meta, this.recordType, o);
16633         }
16634         return this.readRecords(o);
16635     },
16636
16637     // private function a store will implement
16638     onMetaChange : function(meta, recordType, o){
16639
16640     },
16641
16642     /**
16643          * @ignore
16644          */
16645     simpleAccess: function(obj, subsc) {
16646         return obj[subsc];
16647     },
16648
16649         /**
16650          * @ignore
16651          */
16652     getJsonAccessor: function(){
16653         var re = /[\[\.]/;
16654         return function(expr) {
16655             try {
16656                 return(re.test(expr))
16657                     ? new Function("obj", "return obj." + expr)
16658                     : function(obj){
16659                         return obj[expr];
16660                     };
16661             } catch(e){}
16662             return Roo.emptyFn;
16663         };
16664     }(),
16665
16666     /**
16667      * Create a data block containing Roo.data.Records from an XML document.
16668      * @param {Object} o An object which contains an Array of row objects in the property specified
16669      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16670      * which contains the total size of the dataset.
16671      * @return {Object} data A data block which is used by an Roo.data.Store object as
16672      * a cache of Roo.data.Records.
16673      */
16674     readRecords : function(o){
16675         /**
16676          * After any data loads, the raw JSON data is available for further custom processing.
16677          * @type Object
16678          */
16679         this.o = o;
16680         var s = this.meta, Record = this.recordType,
16681             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16682
16683 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16684         if (!this.ef) {
16685             if(s.totalProperty) {
16686                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16687                 }
16688                 if(s.successProperty) {
16689                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16690                 }
16691                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16692                 if (s.id) {
16693                         var g = this.getJsonAccessor(s.id);
16694                         this.getId = function(rec) {
16695                                 var r = g(rec);  
16696                                 return (r === undefined || r === "") ? null : r;
16697                         };
16698                 } else {
16699                         this.getId = function(){return null;};
16700                 }
16701             this.ef = [];
16702             for(var jj = 0; jj < fl; jj++){
16703                 f = fi[jj];
16704                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16705                 this.ef[jj] = this.getJsonAccessor(map);
16706             }
16707         }
16708
16709         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16710         if(s.totalProperty){
16711             var vt = parseInt(this.getTotal(o), 10);
16712             if(!isNaN(vt)){
16713                 totalRecords = vt;
16714             }
16715         }
16716         if(s.successProperty){
16717             var vs = this.getSuccess(o);
16718             if(vs === false || vs === 'false'){
16719                 success = false;
16720             }
16721         }
16722         var records = [];
16723         for(var i = 0; i < c; i++){
16724             var n = root[i];
16725             var values = {};
16726             var id = this.getId(n);
16727             for(var j = 0; j < fl; j++){
16728                 f = fi[j];
16729                                 var v = this.ef[j](n);
16730                                 if (!f.convert) {
16731                                         Roo.log('missing convert for ' + f.name);
16732                                         Roo.log(f);
16733                                         continue;
16734                                 }
16735                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16736             }
16737                         if (!Record) {
16738                                 return {
16739                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16740                                         success : false,
16741                                         records : [],
16742                                         totalRecords : 0
16743                                 };
16744                         }
16745             var record = new Record(values, id);
16746             record.json = n;
16747             records[i] = record;
16748         }
16749         return {
16750             raw : o,
16751             success : success,
16752             records : records,
16753             totalRecords : totalRecords
16754         };
16755     },
16756     // used when loading children.. @see loadDataFromChildren
16757     toLoadData: function(rec)
16758     {
16759         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16760         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16761         return { data : data, total : data.length };
16762         
16763     }
16764 });/*
16765  * Based on:
16766  * Ext JS Library 1.1.1
16767  * Copyright(c) 2006-2007, Ext JS, LLC.
16768  *
16769  * Originally Released Under LGPL - original licence link has changed is not relivant.
16770  *
16771  * Fork - LGPL
16772  * <script type="text/javascript">
16773  */
16774
16775 /**
16776  * @class Roo.data.ArrayReader
16777  * @extends Roo.data.DataReader
16778  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16779  * Each element of that Array represents a row of data fields. The
16780  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16781  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16782  * <p>
16783  * Example code:.
16784  * <pre><code>
16785 var RecordDef = Roo.data.Record.create([
16786     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16787     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16788 ]);
16789 var myReader = new Roo.data.ArrayReader({
16790     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16791 }, RecordDef);
16792 </code></pre>
16793  * <p>
16794  * This would consume an Array like this:
16795  * <pre><code>
16796 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16797   </code></pre>
16798  
16799  * @constructor
16800  * Create a new JsonReader
16801  * @param {Object} meta Metadata configuration options.
16802  * @param {Object|Array} recordType Either an Array of field definition objects
16803  * 
16804  * @cfg {Array} fields Array of field definition objects
16805  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16806  * as specified to {@link Roo.data.Record#create},
16807  * or an {@link Roo.data.Record} object
16808  *
16809  * 
16810  * created using {@link Roo.data.Record#create}.
16811  */
16812 Roo.data.ArrayReader = function(meta, recordType)
16813 {    
16814     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16815 };
16816
16817 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16818     
16819       /**
16820      * Create a data block containing Roo.data.Records from an XML document.
16821      * @param {Object} o An Array of row objects which represents the dataset.
16822      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16823      * a cache of Roo.data.Records.
16824      */
16825     readRecords : function(o)
16826     {
16827         var sid = this.meta ? this.meta.id : null;
16828         var recordType = this.recordType, fields = recordType.prototype.fields;
16829         var records = [];
16830         var root = o;
16831         for(var i = 0; i < root.length; i++){
16832             var n = root[i];
16833             var values = {};
16834             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16835             for(var j = 0, jlen = fields.length; j < jlen; j++){
16836                 var f = fields.items[j];
16837                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16838                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16839                 v = f.convert(v);
16840                 values[f.name] = v;
16841             }
16842             var record = new recordType(values, id);
16843             record.json = n;
16844             records[records.length] = record;
16845         }
16846         return {
16847             records : records,
16848             totalRecords : records.length
16849         };
16850     },
16851     // used when loading children.. @see loadDataFromChildren
16852     toLoadData: function(rec)
16853     {
16854         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16855         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16856         
16857     }
16858     
16859     
16860 });/*
16861  * - LGPL
16862  * * 
16863  */
16864
16865 /**
16866  * @class Roo.bootstrap.form.ComboBox
16867  * @extends Roo.bootstrap.form.TriggerField
16868  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16869  * @cfg {Boolean} append (true|false) default false
16870  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16871  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16872  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16873  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16874  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16875  * @cfg {Boolean} animate default true
16876  * @cfg {Boolean} emptyResultText only for touch device
16877  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16878  * @cfg {String} emptyTitle default ''
16879  * @cfg {Number} width fixed with? experimental
16880  * @constructor
16881  * Create a new ComboBox.
16882  * @param {Object} config Configuration options
16883  */
16884 Roo.bootstrap.form.ComboBox = function(config){
16885     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16886     this.addEvents({
16887         /**
16888          * @event expand
16889          * Fires when the dropdown list is expanded
16890         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16891         */
16892         'expand' : true,
16893         /**
16894          * @event collapse
16895          * Fires when the dropdown list is collapsed
16896         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16897         */
16898         'collapse' : true,
16899         /**
16900          * @event beforeselect
16901          * Fires before a list item is selected. Return false to cancel the selection.
16902         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16903         * @param {Roo.data.Record} record The data record returned from the underlying store
16904         * @param {Number} index The index of the selected item in the dropdown list
16905         */
16906         'beforeselect' : true,
16907         /**
16908          * @event select
16909          * Fires when a list item is selected
16910         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16911         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16912         * @param {Number} index The index of the selected item in the dropdown list
16913         */
16914         'select' : true,
16915         /**
16916          * @event beforequery
16917          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16918          * The event object passed has these properties:
16919         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16920         * @param {String} query The query
16921         * @param {Boolean} forceAll true to force "all" query
16922         * @param {Boolean} cancel true to cancel the query
16923         * @param {Object} e The query event object
16924         */
16925         'beforequery': true,
16926          /**
16927          * @event add
16928          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16929         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16930         */
16931         'add' : true,
16932         /**
16933          * @event edit
16934          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16935         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16936         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16937         */
16938         'edit' : true,
16939         /**
16940          * @event remove
16941          * Fires when the remove value from the combobox array
16942         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16943         */
16944         'remove' : true,
16945         /**
16946          * @event afterremove
16947          * Fires when the remove value from the combobox array
16948         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16949         */
16950         'afterremove' : true,
16951         /**
16952          * @event specialfilter
16953          * Fires when specialfilter
16954             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16955             */
16956         'specialfilter' : true,
16957         /**
16958          * @event tick
16959          * Fires when tick the element
16960             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16961             */
16962         'tick' : true,
16963         /**
16964          * @event touchviewdisplay
16965          * Fires when touch view require special display (default is using displayField)
16966             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16967             * @param {Object} cfg set html .
16968             */
16969         'touchviewdisplay' : true
16970         
16971     });
16972     
16973     this.item = [];
16974     this.tickItems = [];
16975     
16976     this.selectedIndex = -1;
16977     if(this.mode == 'local'){
16978         if(config.queryDelay === undefined){
16979             this.queryDelay = 10;
16980         }
16981         if(config.minChars === undefined){
16982             this.minChars = 0;
16983         }
16984     }
16985 };
16986
16987 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16988      
16989     /**
16990      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16991      * rendering into an Roo.Editor, defaults to false)
16992      */
16993     /**
16994      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16995      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16996      */
16997     /**
16998      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16999      */
17000     /**
17001      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
17002      * the dropdown list (defaults to undefined, with no header element)
17003      */
17004
17005      /**
17006      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
17007      */
17008      
17009      /**
17010      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
17011      */
17012     listWidth: undefined,
17013     /**
17014      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
17015      * mode = 'remote' or 'text' if mode = 'local')
17016      */
17017     displayField: undefined,
17018     
17019     /**
17020      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
17021      * mode = 'remote' or 'value' if mode = 'local'). 
17022      * Note: use of a valueField requires the user make a selection
17023      * in order for a value to be mapped.
17024      */
17025     valueField: undefined,
17026     /**
17027      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
17028      */
17029     modalTitle : '',
17030     
17031     /**
17032      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
17033      * field's data value (defaults to the underlying DOM element's name)
17034      */
17035     hiddenName: undefined,
17036     /**
17037      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17038      */
17039     listClass: '',
17040     /**
17041      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17042      */
17043     selectedClass: 'active',
17044     
17045     /**
17046      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17047      */
17048     shadow:'sides',
17049     /**
17050      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17051      * anchor positions (defaults to 'tl-bl')
17052      */
17053     listAlign: 'tl-bl?',
17054     /**
17055      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17056      */
17057     maxHeight: 300,
17058     /**
17059      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17060      * query specified by the allQuery config option (defaults to 'query')
17061      */
17062     triggerAction: 'query',
17063     /**
17064      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17065      * (defaults to 4, does not apply if editable = false)
17066      */
17067     minChars : 4,
17068     /**
17069      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17070      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17071      */
17072     typeAhead: false,
17073     /**
17074      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17075      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17076      */
17077     queryDelay: 500,
17078     /**
17079      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17080      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17081      */
17082     pageSize: 0,
17083     /**
17084      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17085      * when editable = true (defaults to false)
17086      */
17087     selectOnFocus:false,
17088     /**
17089      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17090      */
17091     queryParam: 'query',
17092     /**
17093      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17094      * when mode = 'remote' (defaults to 'Loading...')
17095      */
17096     loadingText: 'Loading...',
17097     /**
17098      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17099      */
17100     resizable: false,
17101     /**
17102      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17103      */
17104     handleHeight : 8,
17105     /**
17106      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17107      * traditional select (defaults to true)
17108      */
17109     editable: true,
17110     /**
17111      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17112      */
17113     allQuery: '',
17114     /**
17115      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17116      */
17117     mode: 'remote',
17118     /**
17119      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17120      * listWidth has a higher value)
17121      */
17122     minListWidth : 70,
17123     /**
17124      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17125      * allow the user to set arbitrary text into the field (defaults to false)
17126      */
17127     forceSelection:false,
17128     /**
17129      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17130      * if typeAhead = true (defaults to 250)
17131      */
17132     typeAheadDelay : 250,
17133     /**
17134      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17135      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17136      */
17137     valueNotFoundText : undefined,
17138     /**
17139      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17140      */
17141     blockFocus : false,
17142     
17143     /**
17144      * @cfg {Boolean} disableClear Disable showing of clear button.
17145      */
17146     disableClear : false,
17147     /**
17148      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17149      */
17150     alwaysQuery : false,
17151     
17152     /**
17153      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17154      */
17155     multiple : false,
17156     
17157     /**
17158      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17159      */
17160     invalidClass : "has-warning",
17161     
17162     /**
17163      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17164      */
17165     validClass : "has-success",
17166     
17167     /**
17168      * @cfg {Boolean} specialFilter (true|false) special filter default false
17169      */
17170     specialFilter : false,
17171     
17172     /**
17173      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17174      */
17175     mobileTouchView : true,
17176     
17177     /**
17178      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17179      */
17180     useNativeIOS : false,
17181     
17182     /**
17183      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17184      */
17185     mobile_restrict_height : false,
17186     
17187     ios_options : false,
17188     
17189     //private
17190     addicon : false,
17191     editicon: false,
17192     
17193     page: 0,
17194     hasQuery: false,
17195     append: false,
17196     loadNext: false,
17197     autoFocus : true,
17198     tickable : false,
17199     btnPosition : 'right',
17200     triggerList : true,
17201     showToggleBtn : true,
17202     animate : true,
17203     emptyResultText: 'Empty',
17204     triggerText : 'Select',
17205     emptyTitle : '',
17206     width : false,
17207     
17208     // element that contains real text value.. (when hidden is used..)
17209     
17210     getAutoCreate : function()
17211     {   
17212         var cfg = false;
17213         //render
17214         /*
17215          * Render classic select for iso
17216          */
17217         
17218         if(Roo.isIOS && this.useNativeIOS){
17219             cfg = this.getAutoCreateNativeIOS();
17220             return cfg;
17221         }
17222         
17223         /*
17224          * Touch Devices
17225          */
17226         
17227         if(Roo.isTouch && this.mobileTouchView){
17228             cfg = this.getAutoCreateTouchView();
17229             return cfg;;
17230         }
17231         
17232         /*
17233          *  Normal ComboBox
17234          */
17235         if(!this.tickable){
17236             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17237             return cfg;
17238         }
17239         
17240         /*
17241          *  ComboBox with tickable selections
17242          */
17243              
17244         var align = this.labelAlign || this.parentLabelAlign();
17245         
17246         cfg = {
17247             cls : 'form-group roo-combobox-tickable' //input-group
17248         };
17249         
17250         var btn_text_select = '';
17251         var btn_text_done = '';
17252         var btn_text_cancel = '';
17253         
17254         if (this.btn_text_show) {
17255             btn_text_select = 'Select';
17256             btn_text_done = 'Done';
17257             btn_text_cancel = 'Cancel'; 
17258         }
17259         
17260         var buttons = {
17261             tag : 'div',
17262             cls : 'tickable-buttons',
17263             cn : [
17264                 {
17265                     tag : 'button',
17266                     type : 'button',
17267                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17268                     //html : this.triggerText
17269                     html: btn_text_select
17270                 },
17271                 {
17272                     tag : 'button',
17273                     type : 'button',
17274                     name : 'ok',
17275                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17276                     //html : 'Done'
17277                     html: btn_text_done
17278                 },
17279                 {
17280                     tag : 'button',
17281                     type : 'button',
17282                     name : 'cancel',
17283                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17284                     //html : 'Cancel'
17285                     html: btn_text_cancel
17286                 }
17287             ]
17288         };
17289         
17290         if(this.editable){
17291             buttons.cn.unshift({
17292                 tag: 'input',
17293                 cls: 'roo-select2-search-field-input'
17294             });
17295         }
17296         
17297         var _this = this;
17298         
17299         Roo.each(buttons.cn, function(c){
17300             if (_this.size) {
17301                 c.cls += ' btn-' + _this.size;
17302             }
17303
17304             if (_this.disabled) {
17305                 c.disabled = true;
17306             }
17307         });
17308         
17309         var box = {
17310             tag: 'div',
17311             style : 'display: contents',
17312             cn: [
17313                 {
17314                     tag: 'input',
17315                     type : 'hidden',
17316                     cls: 'form-hidden-field'
17317                 },
17318                 {
17319                     tag: 'ul',
17320                     cls: 'roo-select2-choices',
17321                     cn:[
17322                         {
17323                             tag: 'li',
17324                             cls: 'roo-select2-search-field',
17325                             cn: [
17326                                 buttons
17327                             ]
17328                         }
17329                     ]
17330                 }
17331             ]
17332         };
17333         
17334         var combobox = {
17335             cls: 'roo-select2-container input-group roo-select2-container-multi',
17336             cn: [
17337                 
17338                 box
17339 //                {
17340 //                    tag: 'ul',
17341 //                    cls: 'typeahead typeahead-long dropdown-menu',
17342 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17343 //                }
17344             ]
17345         };
17346         
17347         if(this.hasFeedback && !this.allowBlank){
17348             
17349             var feedback = {
17350                 tag: 'span',
17351                 cls: 'glyphicon form-control-feedback'
17352             };
17353
17354             combobox.cn.push(feedback);
17355         }
17356         
17357         
17358         
17359         var indicator = {
17360             tag : 'i',
17361             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17362             tooltip : 'This field is required'
17363         };
17364         if (Roo.bootstrap.version == 4) {
17365             indicator = {
17366                 tag : 'i',
17367                 style : 'display:none'
17368             };
17369         }
17370         if (align ==='left' && this.fieldLabel.length) {
17371             
17372             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17373             
17374             cfg.cn = [
17375                 indicator,
17376                 {
17377                     tag: 'label',
17378                     'for' :  id,
17379                     cls : 'control-label col-form-label',
17380                     html : this.fieldLabel
17381
17382                 },
17383                 {
17384                     cls : "", 
17385                     cn: [
17386                         combobox
17387                     ]
17388                 }
17389
17390             ];
17391             
17392             var labelCfg = cfg.cn[1];
17393             var contentCfg = cfg.cn[2];
17394             
17395
17396             if(this.indicatorpos == 'right'){
17397                 
17398                 cfg.cn = [
17399                     {
17400                         tag: 'label',
17401                         'for' :  id,
17402                         cls : 'control-label col-form-label',
17403                         cn : [
17404                             {
17405                                 tag : 'span',
17406                                 html : this.fieldLabel
17407                             },
17408                             indicator
17409                         ]
17410                     },
17411                     {
17412                         cls : "",
17413                         cn: [
17414                             combobox
17415                         ]
17416                     }
17417
17418                 ];
17419                 
17420                 
17421                 
17422                 labelCfg = cfg.cn[0];
17423                 contentCfg = cfg.cn[1];
17424             
17425             }
17426             
17427             if(this.labelWidth > 12){
17428                 labelCfg.style = "width: " + this.labelWidth + 'px';
17429             }
17430             if(this.width * 1 > 0){
17431                 contentCfg.style = "width: " + this.width + 'px';
17432             }
17433             if(this.labelWidth < 13 && this.labelmd == 0){
17434                 this.labelmd = this.labelWidth;
17435             }
17436             
17437             if(this.labellg > 0){
17438                 labelCfg.cls += ' col-lg-' + this.labellg;
17439                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17440             }
17441             
17442             if(this.labelmd > 0){
17443                 labelCfg.cls += ' col-md-' + this.labelmd;
17444                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17445             }
17446             
17447             if(this.labelsm > 0){
17448                 labelCfg.cls += ' col-sm-' + this.labelsm;
17449                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17450             }
17451             
17452             if(this.labelxs > 0){
17453                 labelCfg.cls += ' col-xs-' + this.labelxs;
17454                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17455             }
17456                 
17457                 
17458         } else if ( this.fieldLabel.length) {
17459 //                Roo.log(" label");
17460                  cfg.cn = [
17461                    indicator,
17462                     {
17463                         tag: 'label',
17464                         //cls : 'input-group-addon',
17465                         html : this.fieldLabel
17466                     },
17467                     combobox
17468                 ];
17469                 
17470                 if(this.indicatorpos == 'right'){
17471                     cfg.cn = [
17472                         {
17473                             tag: 'label',
17474                             //cls : 'input-group-addon',
17475                             html : this.fieldLabel
17476                         },
17477                         indicator,
17478                         combobox
17479                     ];
17480                     
17481                 }
17482
17483         } else {
17484             
17485 //                Roo.log(" no label && no align");
17486                 cfg = combobox
17487                      
17488                 
17489         }
17490          
17491         var settings=this;
17492         ['xs','sm','md','lg'].map(function(size){
17493             if (settings[size]) {
17494                 cfg.cls += ' col-' + size + '-' + settings[size];
17495             }
17496         });
17497         
17498         return cfg;
17499         
17500     },
17501     
17502     _initEventsCalled : false,
17503     
17504     // private
17505     initEvents: function()
17506     {   
17507         if (this._initEventsCalled) { // as we call render... prevent looping...
17508             return;
17509         }
17510         this._initEventsCalled = true;
17511         
17512         if (!this.store) {
17513             throw "can not find store for combo";
17514         }
17515         
17516         this.indicator = this.indicatorEl();
17517         
17518         this.store = Roo.factory(this.store, Roo.data);
17519         this.store.parent = this;
17520         
17521         // if we are building from html. then this element is so complex, that we can not really
17522         // use the rendered HTML.
17523         // so we have to trash and replace the previous code.
17524         if (Roo.XComponent.build_from_html) {
17525             // remove this element....
17526             var e = this.el.dom, k=0;
17527             while (e ) { e = e.previousSibling;  ++k;}
17528
17529             this.el.remove();
17530             
17531             this.el=false;
17532             this.rendered = false;
17533             
17534             this.render(this.parent().getChildContainer(true), k);
17535         }
17536         
17537         if(Roo.isIOS && this.useNativeIOS){
17538             this.initIOSView();
17539             return;
17540         }
17541         
17542         /*
17543          * Touch Devices
17544          */
17545         
17546         if(Roo.isTouch && this.mobileTouchView){
17547             this.initTouchView();
17548             return;
17549         }
17550         
17551         if(this.tickable){
17552             this.initTickableEvents();
17553             return;
17554         }
17555         
17556         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17557         
17558         if(this.hiddenName){
17559             
17560             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17561             
17562             this.hiddenField.dom.value =
17563                 this.hiddenValue !== undefined ? this.hiddenValue :
17564                 this.value !== undefined ? this.value : '';
17565
17566             // prevent input submission
17567             this.el.dom.removeAttribute('name');
17568             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17569              
17570              
17571         }
17572         //if(Roo.isGecko){
17573         //    this.el.dom.setAttribute('autocomplete', 'off');
17574         //}
17575         
17576         var cls = 'x-combo-list';
17577         
17578         //this.list = new Roo.Layer({
17579         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17580         //});
17581         
17582         var _this = this;
17583         
17584         (function(){
17585             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17586             _this.list.setWidth(lw);
17587         }).defer(100);
17588         
17589         this.list.on('mouseover', this.onViewOver, this);
17590         this.list.on('mousemove', this.onViewMove, this);
17591         this.list.on('scroll', this.onViewScroll, this);
17592         
17593         /*
17594         this.list.swallowEvent('mousewheel');
17595         this.assetHeight = 0;
17596
17597         if(this.title){
17598             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17599             this.assetHeight += this.header.getHeight();
17600         }
17601
17602         this.innerList = this.list.createChild({cls:cls+'-inner'});
17603         this.innerList.on('mouseover', this.onViewOver, this);
17604         this.innerList.on('mousemove', this.onViewMove, this);
17605         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17606         
17607         if(this.allowBlank && !this.pageSize && !this.disableClear){
17608             this.footer = this.list.createChild({cls:cls+'-ft'});
17609             this.pageTb = new Roo.Toolbar(this.footer);
17610            
17611         }
17612         if(this.pageSize){
17613             this.footer = this.list.createChild({cls:cls+'-ft'});
17614             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17615                     {pageSize: this.pageSize});
17616             
17617         }
17618         
17619         if (this.pageTb && this.allowBlank && !this.disableClear) {
17620             var _this = this;
17621             this.pageTb.add(new Roo.Toolbar.Fill(), {
17622                 cls: 'x-btn-icon x-btn-clear',
17623                 text: '&#160;',
17624                 handler: function()
17625                 {
17626                     _this.collapse();
17627                     _this.clearValue();
17628                     _this.onSelect(false, -1);
17629                 }
17630             });
17631         }
17632         if (this.footer) {
17633             this.assetHeight += this.footer.getHeight();
17634         }
17635         */
17636             
17637         if(!this.tpl){
17638             this.tpl = Roo.bootstrap.version == 4 ?
17639                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17640                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17641         }
17642
17643         this.view = new Roo.View(this.list, this.tpl, {
17644             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17645         });
17646         //this.view.wrapEl.setDisplayed(false);
17647         this.view.on('click', this.onViewClick, this);
17648         
17649         
17650         this.store.on('beforeload', this.onBeforeLoad, this);
17651         this.store.on('load', this.onLoad, this);
17652         this.store.on('loadexception', this.onLoadException, this);
17653         /*
17654         if(this.resizable){
17655             this.resizer = new Roo.Resizable(this.list,  {
17656                pinned:true, handles:'se'
17657             });
17658             this.resizer.on('resize', function(r, w, h){
17659                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17660                 this.listWidth = w;
17661                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17662                 this.restrictHeight();
17663             }, this);
17664             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17665         }
17666         */
17667         if(!this.editable){
17668             this.editable = true;
17669             this.setEditable(false);
17670         }
17671         
17672         /*
17673         
17674         if (typeof(this.events.add.listeners) != 'undefined') {
17675             
17676             this.addicon = this.wrap.createChild(
17677                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17678        
17679             this.addicon.on('click', function(e) {
17680                 this.fireEvent('add', this);
17681             }, this);
17682         }
17683         if (typeof(this.events.edit.listeners) != 'undefined') {
17684             
17685             this.editicon = this.wrap.createChild(
17686                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17687             if (this.addicon) {
17688                 this.editicon.setStyle('margin-left', '40px');
17689             }
17690             this.editicon.on('click', function(e) {
17691                 
17692                 // we fire even  if inothing is selected..
17693                 this.fireEvent('edit', this, this.lastData );
17694                 
17695             }, this);
17696         }
17697         */
17698         
17699         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17700             "up" : function(e){
17701                 this.inKeyMode = true;
17702                 this.selectPrev();
17703             },
17704
17705             "down" : function(e){
17706                 if(!this.isExpanded()){
17707                     this.onTriggerClick();
17708                 }else{
17709                     this.inKeyMode = true;
17710                     this.selectNext();
17711                 }
17712             },
17713
17714             "enter" : function(e){
17715 //                this.onViewClick();
17716                 //return true;
17717                 this.collapse();
17718                 
17719                 if(this.fireEvent("specialkey", this, e)){
17720                     this.onViewClick(false);
17721                 }
17722                 
17723                 return true;
17724             },
17725
17726             "esc" : function(e){
17727                 this.collapse();
17728             },
17729
17730             "tab" : function(e){
17731                 this.collapse();
17732                 
17733                 if(this.fireEvent("specialkey", this, e)){
17734                     this.onViewClick(false);
17735                 }
17736                 
17737                 return true;
17738             },
17739
17740             scope : this,
17741
17742             doRelay : function(foo, bar, hname){
17743                 if(hname == 'down' || this.scope.isExpanded()){
17744                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17745                 }
17746                 return true;
17747             },
17748
17749             forceKeyDown: true
17750         });
17751         
17752         
17753         this.queryDelay = Math.max(this.queryDelay || 10,
17754                 this.mode == 'local' ? 10 : 250);
17755         
17756         
17757         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17758         
17759         if(this.typeAhead){
17760             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17761         }
17762         if(this.editable !== false){
17763             this.inputEl().on("keyup", this.onKeyUp, this);
17764         }
17765         if(this.forceSelection){
17766             this.inputEl().on('blur', this.doForce, this);
17767         }
17768         
17769         if(this.multiple){
17770             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17771             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17772         }
17773     },
17774     
17775     initTickableEvents: function()
17776     {   
17777         this.createList();
17778         
17779         if(this.hiddenName){
17780             
17781             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17782             
17783             this.hiddenField.dom.value =
17784                 this.hiddenValue !== undefined ? this.hiddenValue :
17785                 this.value !== undefined ? this.value : '';
17786
17787             // prevent input submission
17788             this.el.dom.removeAttribute('name');
17789             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17790              
17791              
17792         }
17793         
17794 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17795         
17796         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17797         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17798         if(this.triggerList){
17799             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17800         }
17801          
17802         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17803         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17804         
17805         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17806         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17807         
17808         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17809         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17810         
17811         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17812         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17813         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17814         
17815         this.okBtn.hide();
17816         this.cancelBtn.hide();
17817         
17818         var _this = this;
17819         
17820         (function(){
17821             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17822             _this.list.setWidth(lw);
17823         }).defer(100);
17824         
17825         this.list.on('mouseover', this.onViewOver, this);
17826         this.list.on('mousemove', this.onViewMove, this);
17827         
17828         this.list.on('scroll', this.onViewScroll, this);
17829         
17830         if(!this.tpl){
17831             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17832                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17833         }
17834
17835         this.view = new Roo.View(this.list, this.tpl, {
17836             singleSelect:true,
17837             tickable:true,
17838             parent:this,
17839             store: this.store,
17840             selectedClass: this.selectedClass
17841         });
17842         
17843         //this.view.wrapEl.setDisplayed(false);
17844         this.view.on('click', this.onViewClick, this);
17845         
17846         
17847         
17848         this.store.on('beforeload', this.onBeforeLoad, this);
17849         this.store.on('load', this.onLoad, this);
17850         this.store.on('loadexception', this.onLoadException, this);
17851         
17852         if(this.editable){
17853             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17854                 "up" : function(e){
17855                     this.inKeyMode = true;
17856                     this.selectPrev();
17857                 },
17858
17859                 "down" : function(e){
17860                     this.inKeyMode = true;
17861                     this.selectNext();
17862                 },
17863
17864                 "enter" : function(e){
17865                     if(this.fireEvent("specialkey", this, e)){
17866                         this.onViewClick(false);
17867                     }
17868                     
17869                     return true;
17870                 },
17871
17872                 "esc" : function(e){
17873                     this.onTickableFooterButtonClick(e, false, false);
17874                 },
17875
17876                 "tab" : function(e){
17877                     this.fireEvent("specialkey", this, e);
17878                     
17879                     this.onTickableFooterButtonClick(e, false, false);
17880                     
17881                     return true;
17882                 },
17883
17884                 scope : this,
17885
17886                 doRelay : function(e, fn, key){
17887                     if(this.scope.isExpanded()){
17888                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17889                     }
17890                     return true;
17891                 },
17892
17893                 forceKeyDown: true
17894             });
17895         }
17896         
17897         this.queryDelay = Math.max(this.queryDelay || 10,
17898                 this.mode == 'local' ? 10 : 250);
17899         
17900         
17901         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17902         
17903         if(this.typeAhead){
17904             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17905         }
17906         
17907         if(this.editable !== false){
17908             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17909         }
17910         
17911         this.indicator = this.indicatorEl();
17912         
17913         if(this.indicator){
17914             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17915             this.indicator.hide();
17916         }
17917         
17918     },
17919
17920     onDestroy : function(){
17921         if(this.view){
17922             this.view.setStore(null);
17923             this.view.el.removeAllListeners();
17924             this.view.el.remove();
17925             this.view.purgeListeners();
17926         }
17927         if(this.list){
17928             this.list.dom.innerHTML  = '';
17929         }
17930         
17931         if(this.store){
17932             this.store.un('beforeload', this.onBeforeLoad, this);
17933             this.store.un('load', this.onLoad, this);
17934             this.store.un('loadexception', this.onLoadException, this);
17935         }
17936         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17937     },
17938
17939     // private
17940     fireKey : function(e){
17941         if(e.isNavKeyPress() && !this.list.isVisible()){
17942             this.fireEvent("specialkey", this, e);
17943         }
17944     },
17945
17946     // private
17947     onResize: function(w, h)
17948     {
17949         
17950         
17951 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17952 //        
17953 //        if(typeof w != 'number'){
17954 //            // we do not handle it!?!?
17955 //            return;
17956 //        }
17957 //        var tw = this.trigger.getWidth();
17958 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17959 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17960 //        var x = w - tw;
17961 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17962 //            
17963 //        //this.trigger.setStyle('left', x+'px');
17964 //        
17965 //        if(this.list && this.listWidth === undefined){
17966 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17967 //            this.list.setWidth(lw);
17968 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17969 //        }
17970         
17971     
17972         
17973     },
17974
17975     /**
17976      * Allow or prevent the user from directly editing the field text.  If false is passed,
17977      * the user will only be able to select from the items defined in the dropdown list.  This method
17978      * is the runtime equivalent of setting the 'editable' config option at config time.
17979      * @param {Boolean} value True to allow the user to directly edit the field text
17980      */
17981     setEditable : function(value){
17982         if(value == this.editable){
17983             return;
17984         }
17985         this.editable = value;
17986         if(!value){
17987             this.inputEl().dom.setAttribute('readOnly', true);
17988             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17989             this.inputEl().addClass('x-combo-noedit');
17990         }else{
17991             this.inputEl().dom.removeAttribute('readOnly');
17992             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17993             this.inputEl().removeClass('x-combo-noedit');
17994         }
17995     },
17996
17997     // private
17998     
17999     onBeforeLoad : function(combo,opts){
18000         if(!this.hasFocus){
18001             return;
18002         }
18003          if (!opts.add) {
18004             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
18005          }
18006         this.restrictHeight();
18007         this.selectedIndex = -1;
18008     },
18009
18010     // private
18011     onLoad : function(){
18012         
18013         this.hasQuery = false;
18014         
18015         if(!this.hasFocus){
18016             return;
18017         }
18018         
18019         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18020             this.loading.hide();
18021         }
18022         
18023         if(this.store.getCount() > 0){
18024             
18025             this.expand();
18026             this.restrictHeight();
18027             if(this.lastQuery == this.allQuery){
18028                 if(this.editable && !this.tickable){
18029                     this.inputEl().dom.select();
18030                 }
18031                 
18032                 if(
18033                     !this.selectByValue(this.value, true) &&
18034                     this.autoFocus && 
18035                     (
18036                         !this.store.lastOptions ||
18037                         typeof(this.store.lastOptions.add) == 'undefined' || 
18038                         this.store.lastOptions.add != true
18039                     )
18040                 ){
18041                     this.select(0, true);
18042                 }
18043             }else{
18044                 if(this.autoFocus){
18045                     this.selectNext();
18046                 }
18047                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18048                     this.taTask.delay(this.typeAheadDelay);
18049                 }
18050             }
18051         }else{
18052             this.onEmptyResults();
18053         }
18054         
18055         //this.el.focus();
18056     },
18057     // private
18058     onLoadException : function()
18059     {
18060         this.hasQuery = false;
18061         
18062         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18063             this.loading.hide();
18064         }
18065         
18066         if(this.tickable && this.editable){
18067             return;
18068         }
18069         
18070         this.collapse();
18071         // only causes errors at present
18072         //Roo.log(this.store.reader.jsonData);
18073         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18074             // fixme
18075             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18076         //}
18077         
18078         
18079     },
18080     // private
18081     onTypeAhead : function(){
18082         if(this.store.getCount() > 0){
18083             var r = this.store.getAt(0);
18084             var newValue = r.data[this.displayField];
18085             var len = newValue.length;
18086             var selStart = this.getRawValue().length;
18087             
18088             if(selStart != len){
18089                 this.setRawValue(newValue);
18090                 this.selectText(selStart, newValue.length);
18091             }
18092         }
18093     },
18094
18095     // private
18096     onSelect : function(record, index){
18097         
18098         if(this.fireEvent('beforeselect', this, record, index) !== false){
18099         
18100             this.setFromData(index > -1 ? record.data : false);
18101             
18102             this.collapse();
18103             this.fireEvent('select', this, record, index);
18104         }
18105     },
18106
18107     /**
18108      * Returns the currently selected field value or empty string if no value is set.
18109      * @return {String} value The selected value
18110      */
18111     getValue : function()
18112     {
18113         if(Roo.isIOS && this.useNativeIOS){
18114             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18115         }
18116         
18117         if(this.multiple){
18118             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18119         }
18120         
18121         if(this.valueField){
18122             return typeof this.value != 'undefined' ? this.value : '';
18123         }else{
18124             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18125         }
18126     },
18127     
18128     getRawValue : function()
18129     {
18130         if(Roo.isIOS && this.useNativeIOS){
18131             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18132         }
18133         
18134         var v = this.inputEl().getValue();
18135         
18136         return v;
18137     },
18138
18139     /**
18140      * Clears any text/value currently set in the field
18141      */
18142     clearValue : function(){
18143         
18144         if(this.hiddenField){
18145             this.hiddenField.dom.value = '';
18146         }
18147         this.value = '';
18148         this.setRawValue('');
18149         this.lastSelectionText = '';
18150         this.lastData = false;
18151         
18152         var close = this.closeTriggerEl();
18153         
18154         if(close){
18155             close.hide();
18156         }
18157         
18158         this.validate();
18159         
18160     },
18161
18162     /**
18163      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18164      * will be displayed in the field.  If the value does not match the data value of an existing item,
18165      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18166      * Otherwise the field will be blank (although the value will still be set).
18167      * @param {String} value The value to match
18168      */
18169     setValue : function(v)
18170     {
18171         if(Roo.isIOS && this.useNativeIOS){
18172             this.setIOSValue(v);
18173             return;
18174         }
18175         
18176         if(this.multiple){
18177             this.syncValue();
18178             return;
18179         }
18180         
18181         var text = v;
18182         if(this.valueField){
18183             var r = this.findRecord(this.valueField, v);
18184             if(r){
18185                 text = r.data[this.displayField];
18186             }else if(this.valueNotFoundText !== undefined){
18187                 text = this.valueNotFoundText;
18188             }
18189         }
18190         this.lastSelectionText = text;
18191         if(this.hiddenField){
18192             this.hiddenField.dom.value = v;
18193         }
18194         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18195         this.value = v;
18196         
18197         var close = this.closeTriggerEl();
18198         
18199         if(close){
18200             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18201         }
18202         
18203         this.validate();
18204     },
18205     /**
18206      * @property {Object} the last set data for the element
18207      */
18208     
18209     lastData : false,
18210     /**
18211      * Sets the value of the field based on a object which is related to the record format for the store.
18212      * @param {Object} value the value to set as. or false on reset?
18213      */
18214     setFromData : function(o){
18215         
18216         if(this.multiple){
18217             this.addItem(o);
18218             return;
18219         }
18220             
18221         var dv = ''; // display value
18222         var vv = ''; // value value..
18223         this.lastData = o;
18224         if (this.displayField) {
18225             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18226         } else {
18227             // this is an error condition!!!
18228             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18229         }
18230         
18231         if(this.valueField){
18232             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18233         }
18234         
18235         var close = this.closeTriggerEl();
18236         
18237         if(close){
18238             if(dv.length || vv * 1 > 0){
18239                 close.show() ;
18240                 this.blockFocus=true;
18241             } else {
18242                 close.hide();
18243             }             
18244         }
18245         
18246         if(this.hiddenField){
18247             this.hiddenField.dom.value = vv;
18248             
18249             this.lastSelectionText = dv;
18250             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18251             this.value = vv;
18252             return;
18253         }
18254         // no hidden field.. - we store the value in 'value', but still display
18255         // display field!!!!
18256         this.lastSelectionText = dv;
18257         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18258         this.value = vv;
18259         
18260         
18261         
18262     },
18263     // private
18264     reset : function(){
18265         // overridden so that last data is reset..
18266         
18267         if(this.multiple){
18268             this.clearItem();
18269             return;
18270         }
18271         
18272         this.setValue(this.originalValue);
18273         //this.clearInvalid();
18274         this.lastData = false;
18275         if (this.view) {
18276             this.view.clearSelections();
18277         }
18278         
18279         this.validate();
18280     },
18281     // private
18282     findRecord : function(prop, value){
18283         var record;
18284         if(this.store.getCount() > 0){
18285             this.store.each(function(r){
18286                 if(r.data[prop] == value){
18287                     record = r;
18288                     return false;
18289                 }
18290                 return true;
18291             });
18292         }
18293         return record;
18294     },
18295     
18296     getName: function()
18297     {
18298         // returns hidden if it's set..
18299         if (!this.rendered) {return ''};
18300         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18301         
18302     },
18303     // private
18304     onViewMove : function(e, t){
18305         this.inKeyMode = false;
18306     },
18307
18308     // private
18309     onViewOver : function(e, t){
18310         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18311             return;
18312         }
18313         var item = this.view.findItemFromChild(t);
18314         
18315         if(item){
18316             var index = this.view.indexOf(item);
18317             this.select(index, false);
18318         }
18319     },
18320
18321     // private
18322     onViewClick : function(view, doFocus, el, e)
18323     {
18324         var index = this.view.getSelectedIndexes()[0];
18325         
18326         var r = this.store.getAt(index);
18327         
18328         if(this.tickable){
18329             
18330             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18331                 return;
18332             }
18333             
18334             var rm = false;
18335             var _this = this;
18336             
18337             Roo.each(this.tickItems, function(v,k){
18338                 
18339                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18340                     Roo.log(v);
18341                     _this.tickItems.splice(k, 1);
18342                     
18343                     if(typeof(e) == 'undefined' && view == false){
18344                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18345                     }
18346                     
18347                     rm = true;
18348                     return;
18349                 }
18350             });
18351             
18352             if(rm){
18353                 return;
18354             }
18355             
18356             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18357                 this.tickItems.push(r.data);
18358             }
18359             
18360             if(typeof(e) == 'undefined' && view == false){
18361                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18362             }
18363                     
18364             return;
18365         }
18366         
18367         if(r){
18368             this.onSelect(r, index);
18369         }
18370         if(doFocus !== false && !this.blockFocus){
18371             this.inputEl().focus();
18372         }
18373     },
18374
18375     // private
18376     restrictHeight : function(){
18377         //this.innerList.dom.style.height = '';
18378         //var inner = this.innerList.dom;
18379         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18380         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18381         //this.list.beginUpdate();
18382         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18383         this.list.alignTo(this.inputEl(), this.listAlign);
18384         this.list.alignTo(this.inputEl(), this.listAlign);
18385         //this.list.endUpdate();
18386     },
18387
18388     // private
18389     onEmptyResults : function(){
18390         
18391         if(this.tickable && this.editable){
18392             this.hasFocus = false;
18393             this.restrictHeight();
18394             return;
18395         }
18396         
18397         this.collapse();
18398     },
18399
18400     /**
18401      * Returns true if the dropdown list is expanded, else false.
18402      */
18403     isExpanded : function(){
18404         return this.list.isVisible();
18405     },
18406
18407     /**
18408      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18409      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18410      * @param {String} value The data value of the item to select
18411      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18412      * selected item if it is not currently in view (defaults to true)
18413      * @return {Boolean} True if the value matched an item in the list, else false
18414      */
18415     selectByValue : function(v, scrollIntoView){
18416         if(v !== undefined && v !== null){
18417             var r = this.findRecord(this.valueField || this.displayField, v);
18418             if(r){
18419                 this.select(this.store.indexOf(r), scrollIntoView);
18420                 return true;
18421             }
18422         }
18423         return false;
18424     },
18425
18426     /**
18427      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18428      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18429      * @param {Number} index The zero-based index of the list item to select
18430      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18431      * selected item if it is not currently in view (defaults to true)
18432      */
18433     select : function(index, scrollIntoView){
18434         this.selectedIndex = index;
18435         this.view.select(index);
18436         if(scrollIntoView !== false){
18437             var el = this.view.getNode(index);
18438             /*
18439              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18440              */
18441             if(el){
18442                 this.list.scrollChildIntoView(el, false);
18443             }
18444         }
18445     },
18446
18447     // private
18448     selectNext : function(){
18449         var ct = this.store.getCount();
18450         if(ct > 0){
18451             if(this.selectedIndex == -1){
18452                 this.select(0);
18453             }else if(this.selectedIndex < ct-1){
18454                 this.select(this.selectedIndex+1);
18455             }
18456         }
18457     },
18458
18459     // private
18460     selectPrev : function(){
18461         var ct = this.store.getCount();
18462         if(ct > 0){
18463             if(this.selectedIndex == -1){
18464                 this.select(0);
18465             }else if(this.selectedIndex != 0){
18466                 this.select(this.selectedIndex-1);
18467             }
18468         }
18469     },
18470
18471     // private
18472     onKeyUp : function(e){
18473         if(this.editable !== false && !e.isSpecialKey()){
18474             this.lastKey = e.getKey();
18475             this.dqTask.delay(this.queryDelay);
18476         }
18477     },
18478
18479     // private
18480     validateBlur : function(){
18481         return !this.list || !this.list.isVisible();   
18482     },
18483
18484     // private
18485     initQuery : function(){
18486         
18487         var v = this.getRawValue();
18488         
18489         if(this.tickable && this.editable){
18490             v = this.tickableInputEl().getValue();
18491         }
18492         
18493         this.doQuery(v);
18494     },
18495
18496     // private
18497     doForce : function(){
18498         if(this.inputEl().dom.value.length > 0){
18499             this.inputEl().dom.value =
18500                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18501              
18502         }
18503     },
18504
18505     /**
18506      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18507      * query allowing the query action to be canceled if needed.
18508      * @param {String} query The SQL query to execute
18509      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18510      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18511      * saved in the current store (defaults to false)
18512      */
18513     doQuery : function(q, forceAll){
18514         
18515         if(q === undefined || q === null){
18516             q = '';
18517         }
18518         var qe = {
18519             query: q,
18520             forceAll: forceAll,
18521             combo: this,
18522             cancel:false
18523         };
18524         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18525             return false;
18526         }
18527         q = qe.query;
18528         
18529         forceAll = qe.forceAll;
18530         if(forceAll === true || (q.length >= this.minChars)){
18531             
18532             this.hasQuery = true;
18533             
18534             if(this.lastQuery != q || this.alwaysQuery){
18535                 this.lastQuery = q;
18536                 if(this.mode == 'local'){
18537                     this.selectedIndex = -1;
18538                     if(forceAll){
18539                         this.store.clearFilter();
18540                     }else{
18541                         
18542                         if(this.specialFilter){
18543                             this.fireEvent('specialfilter', this);
18544                             this.onLoad();
18545                             return;
18546                         }
18547                         
18548                         this.store.filter(this.displayField, q);
18549                     }
18550                     
18551                     this.store.fireEvent("datachanged", this.store);
18552                     
18553                     this.onLoad();
18554                     
18555                     
18556                 }else{
18557                     
18558                     this.store.baseParams[this.queryParam] = q;
18559                     
18560                     var options = {params : this.getParams(q)};
18561                     
18562                     if(this.loadNext){
18563                         options.add = true;
18564                         options.params.start = this.page * this.pageSize;
18565                     }
18566                     
18567                     this.store.load(options);
18568                     
18569                     /*
18570                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18571                      *  we should expand the list on onLoad
18572                      *  so command out it
18573                      */
18574 //                    this.expand();
18575                 }
18576             }else{
18577                 this.selectedIndex = -1;
18578                 this.onLoad();   
18579             }
18580         }
18581         
18582         this.loadNext = false;
18583     },
18584     
18585     // private
18586     getParams : function(q){
18587         var p = {};
18588         //p[this.queryParam] = q;
18589         
18590         if(this.pageSize){
18591             p.start = 0;
18592             p.limit = this.pageSize;
18593         }
18594         return p;
18595     },
18596
18597     /**
18598      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18599      */
18600     collapse : function(){
18601         if(!this.isExpanded()){
18602             return;
18603         }
18604         
18605         this.list.hide();
18606         
18607         this.hasFocus = false;
18608         
18609         if(this.tickable){
18610             this.okBtn.hide();
18611             this.cancelBtn.hide();
18612             this.trigger.show();
18613             
18614             if(this.editable){
18615                 this.tickableInputEl().dom.value = '';
18616                 this.tickableInputEl().blur();
18617             }
18618             
18619         }
18620         
18621         Roo.get(document).un('mousedown', this.collapseIf, this);
18622         Roo.get(document).un('mousewheel', this.collapseIf, this);
18623         if (!this.editable) {
18624             Roo.get(document).un('keydown', this.listKeyPress, this);
18625         }
18626         this.fireEvent('collapse', this);
18627         
18628         this.validate();
18629     },
18630
18631     // private
18632     collapseIf : function(e){
18633         var in_combo  = e.within(this.el);
18634         var in_list =  e.within(this.list);
18635         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18636         
18637         if (in_combo || in_list || is_list) {
18638             //e.stopPropagation();
18639             return;
18640         }
18641         
18642         if(this.tickable){
18643             this.onTickableFooterButtonClick(e, false, false);
18644         }
18645
18646         this.collapse();
18647         
18648     },
18649
18650     /**
18651      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18652      */
18653     expand : function(){
18654        
18655         if(this.isExpanded() || !this.hasFocus){
18656             return;
18657         }
18658         
18659         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18660         this.list.setWidth(lw);
18661         
18662         Roo.log('expand');
18663         
18664         this.list.show();
18665         
18666         this.restrictHeight();
18667         
18668         if(this.tickable){
18669             
18670             this.tickItems = Roo.apply([], this.item);
18671             
18672             this.okBtn.show();
18673             this.cancelBtn.show();
18674             this.trigger.hide();
18675             
18676             if(this.editable){
18677                 this.tickableInputEl().focus();
18678             }
18679             
18680         }
18681         
18682         Roo.get(document).on('mousedown', this.collapseIf, this);
18683         Roo.get(document).on('mousewheel', this.collapseIf, this);
18684         if (!this.editable) {
18685             Roo.get(document).on('keydown', this.listKeyPress, this);
18686         }
18687         
18688         this.fireEvent('expand', this);
18689     },
18690
18691     // private
18692     // Implements the default empty TriggerField.onTriggerClick function
18693     onTriggerClick : function(e)
18694     {
18695         Roo.log('trigger click');
18696         
18697         if(this.disabled || !this.triggerList){
18698             return;
18699         }
18700         
18701         this.page = 0;
18702         this.loadNext = false;
18703         
18704         if(this.isExpanded()){
18705             this.collapse();
18706             if (!this.blockFocus) {
18707                 this.inputEl().focus();
18708             }
18709             
18710         }else {
18711             this.hasFocus = true;
18712             if(this.triggerAction == 'all') {
18713                 this.doQuery(this.allQuery, true);
18714             } else {
18715                 this.doQuery(this.getRawValue());
18716             }
18717             if (!this.blockFocus) {
18718                 this.inputEl().focus();
18719             }
18720         }
18721     },
18722     
18723     onTickableTriggerClick : function(e)
18724     {
18725         if(this.disabled){
18726             return;
18727         }
18728         
18729         this.page = 0;
18730         this.loadNext = false;
18731         this.hasFocus = true;
18732         
18733         if(this.triggerAction == 'all') {
18734             this.doQuery(this.allQuery, true);
18735         } else {
18736             this.doQuery(this.getRawValue());
18737         }
18738     },
18739     
18740     onSearchFieldClick : function(e)
18741     {
18742         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18743             this.onTickableFooterButtonClick(e, false, false);
18744             return;
18745         }
18746         
18747         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18748             return;
18749         }
18750         
18751         this.page = 0;
18752         this.loadNext = false;
18753         this.hasFocus = true;
18754         
18755         if(this.triggerAction == 'all') {
18756             this.doQuery(this.allQuery, true);
18757         } else {
18758             this.doQuery(this.getRawValue());
18759         }
18760     },
18761     
18762     listKeyPress : function(e)
18763     {
18764         //Roo.log('listkeypress');
18765         // scroll to first matching element based on key pres..
18766         if (e.isSpecialKey()) {
18767             return false;
18768         }
18769         var k = String.fromCharCode(e.getKey()).toUpperCase();
18770         //Roo.log(k);
18771         var match  = false;
18772         var csel = this.view.getSelectedNodes();
18773         var cselitem = false;
18774         if (csel.length) {
18775             var ix = this.view.indexOf(csel[0]);
18776             cselitem  = this.store.getAt(ix);
18777             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18778                 cselitem = false;
18779             }
18780             
18781         }
18782         
18783         this.store.each(function(v) { 
18784             if (cselitem) {
18785                 // start at existing selection.
18786                 if (cselitem.id == v.id) {
18787                     cselitem = false;
18788                 }
18789                 return true;
18790             }
18791                 
18792             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18793                 match = this.store.indexOf(v);
18794                 return false;
18795             }
18796             return true;
18797         }, this);
18798         
18799         if (match === false) {
18800             return true; // no more action?
18801         }
18802         // scroll to?
18803         this.view.select(match);
18804         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18805         sn.scrollIntoView(sn.dom.parentNode, false);
18806     },
18807     
18808     onViewScroll : function(e, t){
18809         
18810         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){
18811             return;
18812         }
18813         
18814         this.hasQuery = true;
18815         
18816         this.loading = this.list.select('.loading', true).first();
18817         
18818         if(this.loading === null){
18819             this.list.createChild({
18820                 tag: 'div',
18821                 cls: 'loading roo-select2-more-results roo-select2-active',
18822                 html: 'Loading more results...'
18823             });
18824             
18825             this.loading = this.list.select('.loading', true).first();
18826             
18827             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18828             
18829             this.loading.hide();
18830         }
18831         
18832         this.loading.show();
18833         
18834         var _combo = this;
18835         
18836         this.page++;
18837         this.loadNext = true;
18838         
18839         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18840         
18841         return;
18842     },
18843     
18844     addItem : function(o)
18845     {   
18846         var dv = ''; // display value
18847         
18848         if (this.displayField) {
18849             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18850         } else {
18851             // this is an error condition!!!
18852             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18853         }
18854         
18855         if(!dv.length){
18856             return;
18857         }
18858         
18859         var choice = this.choices.createChild({
18860             tag: 'li',
18861             cls: 'roo-select2-search-choice',
18862             cn: [
18863                 {
18864                     tag: 'div',
18865                     html: dv
18866                 },
18867                 {
18868                     tag: 'a',
18869                     href: '#',
18870                     cls: 'roo-select2-search-choice-close fa fa-times',
18871                     tabindex: '-1'
18872                 }
18873             ]
18874             
18875         }, this.searchField);
18876         
18877         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18878         
18879         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18880         
18881         this.item.push(o);
18882         
18883         this.lastData = o;
18884         
18885         this.syncValue();
18886         
18887         this.inputEl().dom.value = '';
18888         
18889         this.validate();
18890     },
18891     
18892     onRemoveItem : function(e, _self, o)
18893     {
18894         e.preventDefault();
18895         
18896         this.lastItem = Roo.apply([], this.item);
18897         
18898         var index = this.item.indexOf(o.data) * 1;
18899         
18900         if( index < 0){
18901             Roo.log('not this item?!');
18902             return;
18903         }
18904         
18905         this.item.splice(index, 1);
18906         o.item.remove();
18907         
18908         this.syncValue();
18909         
18910         this.fireEvent('remove', this, e);
18911         
18912         this.validate();
18913         
18914     },
18915     
18916     syncValue : function()
18917     {
18918         if(!this.item.length){
18919             this.clearValue();
18920             return;
18921         }
18922             
18923         var value = [];
18924         var _this = this;
18925         Roo.each(this.item, function(i){
18926             if(_this.valueField){
18927                 value.push(i[_this.valueField]);
18928                 return;
18929             }
18930
18931             value.push(i);
18932         });
18933
18934         this.value = value.join(',');
18935
18936         if(this.hiddenField){
18937             this.hiddenField.dom.value = this.value;
18938         }
18939         
18940         this.store.fireEvent("datachanged", this.store);
18941         
18942         this.validate();
18943     },
18944     
18945     clearItem : function()
18946     {
18947         if(!this.multiple){
18948             return;
18949         }
18950         
18951         this.item = [];
18952         
18953         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18954            c.remove();
18955         });
18956         
18957         this.syncValue();
18958         
18959         this.validate();
18960         
18961         if(this.tickable && !Roo.isTouch){
18962             this.view.refresh();
18963         }
18964     },
18965     
18966     inputEl: function ()
18967     {
18968         if(Roo.isIOS && this.useNativeIOS){
18969             return this.el.select('select.roo-ios-select', true).first();
18970         }
18971         
18972         if(Roo.isTouch && this.mobileTouchView){
18973             return this.el.select('input.form-control',true).first();
18974         }
18975         
18976         if(this.tickable){
18977             return this.searchField;
18978         }
18979         
18980         return this.el.select('input.form-control',true).first();
18981     },
18982     
18983     onTickableFooterButtonClick : function(e, btn, el)
18984     {
18985         e.preventDefault();
18986         
18987         this.lastItem = Roo.apply([], this.item);
18988         
18989         if(btn && btn.name == 'cancel'){
18990             this.tickItems = Roo.apply([], this.item);
18991             this.collapse();
18992             return;
18993         }
18994         
18995         this.clearItem();
18996         
18997         var _this = this;
18998         
18999         Roo.each(this.tickItems, function(o){
19000             _this.addItem(o);
19001         });
19002         
19003         this.collapse();
19004         
19005     },
19006     
19007     validate : function()
19008     {
19009         if(this.getVisibilityEl().hasClass('hidden')){
19010             return true;
19011         }
19012         
19013         var v = this.getRawValue();
19014         
19015         if(this.multiple){
19016             v = this.getValue();
19017         }
19018         
19019         if(this.disabled || this.allowBlank || v.length){
19020             this.markValid();
19021             return true;
19022         }
19023         
19024         this.markInvalid();
19025         return false;
19026     },
19027     
19028     tickableInputEl : function()
19029     {
19030         if(!this.tickable || !this.editable){
19031             return this.inputEl();
19032         }
19033         
19034         return this.inputEl().select('.roo-select2-search-field-input', true).first();
19035     },
19036     
19037     
19038     getAutoCreateTouchView : function()
19039     {
19040         var id = Roo.id();
19041         
19042         var cfg = {
19043             cls: 'form-group' //input-group
19044         };
19045         
19046         var input =  {
19047             tag: 'input',
19048             id : id,
19049             type : this.inputType,
19050             cls : 'form-control x-combo-noedit',
19051             autocomplete: 'new-password',
19052             placeholder : this.placeholder || '',
19053             readonly : true
19054         };
19055         
19056         if (this.name) {
19057             input.name = this.name;
19058         }
19059         
19060         if (this.size) {
19061             input.cls += ' input-' + this.size;
19062         }
19063         
19064         if (this.disabled) {
19065             input.disabled = true;
19066         }
19067         
19068         var inputblock = {
19069             cls : 'roo-combobox-wrap',
19070             cn : [
19071                 input
19072             ]
19073         };
19074         
19075         if(this.before){
19076             inputblock.cls += ' input-group';
19077             
19078             inputblock.cn.unshift({
19079                 tag :'span',
19080                 cls : 'input-group-addon input-group-prepend input-group-text',
19081                 html : this.before
19082             });
19083         }
19084         
19085         if(this.removable && !this.multiple){
19086             inputblock.cls += ' roo-removable';
19087             
19088             inputblock.cn.push({
19089                 tag: 'button',
19090                 html : 'x',
19091                 cls : 'roo-combo-removable-btn close'
19092             });
19093         }
19094
19095         if(this.hasFeedback && !this.allowBlank){
19096             
19097             inputblock.cls += ' has-feedback';
19098             
19099             inputblock.cn.push({
19100                 tag: 'span',
19101                 cls: 'glyphicon form-control-feedback'
19102             });
19103             
19104         }
19105         
19106         if (this.after) {
19107             
19108             inputblock.cls += (this.before) ? '' : ' input-group';
19109             
19110             inputblock.cn.push({
19111                 tag :'span',
19112                 cls : 'input-group-addon input-group-append input-group-text',
19113                 html : this.after
19114             });
19115         }
19116
19117         
19118         var ibwrap = inputblock;
19119         
19120         if(this.multiple){
19121             ibwrap = {
19122                 tag: 'ul',
19123                 cls: 'roo-select2-choices',
19124                 cn:[
19125                     {
19126                         tag: 'li',
19127                         cls: 'roo-select2-search-field',
19128                         cn: [
19129
19130                             inputblock
19131                         ]
19132                     }
19133                 ]
19134             };
19135         
19136             
19137         }
19138         
19139         var combobox = {
19140             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19141             cn: [
19142                 {
19143                     tag: 'input',
19144                     type : 'hidden',
19145                     cls: 'form-hidden-field'
19146                 },
19147                 ibwrap
19148             ]
19149         };
19150         
19151         if(!this.multiple && this.showToggleBtn){
19152             
19153             var caret = {
19154                 cls: 'caret'
19155             };
19156             
19157             if (this.caret != false) {
19158                 caret = {
19159                      tag: 'i',
19160                      cls: 'fa fa-' + this.caret
19161                 };
19162                 
19163             }
19164             
19165             combobox.cn.push({
19166                 tag :'span',
19167                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19168                 cn : [
19169                     Roo.bootstrap.version == 3 ? caret : '',
19170                     {
19171                         tag: 'span',
19172                         cls: 'combobox-clear',
19173                         cn  : [
19174                             {
19175                                 tag : 'i',
19176                                 cls: 'icon-remove'
19177                             }
19178                         ]
19179                     }
19180                 ]
19181
19182             })
19183         }
19184         
19185         if(this.multiple){
19186             combobox.cls += ' roo-select2-container-multi';
19187         }
19188         
19189         var required =  this.allowBlank ?  {
19190                     tag : 'i',
19191                     style: 'display: none'
19192                 } : {
19193                    tag : 'i',
19194                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19195                    tooltip : 'This field is required'
19196                 };
19197         
19198         var align = this.labelAlign || this.parentLabelAlign();
19199         
19200         if (align ==='left' && this.fieldLabel.length) {
19201
19202             cfg.cn = [
19203                 required,
19204                 {
19205                     tag: 'label',
19206                     cls : 'control-label col-form-label',
19207                     html : this.fieldLabel
19208
19209                 },
19210                 {
19211                     cls : 'roo-combobox-wrap ', 
19212                     cn: [
19213                         combobox
19214                     ]
19215                 }
19216             ];
19217             
19218             var labelCfg = cfg.cn[1];
19219             var contentCfg = cfg.cn[2];
19220             
19221
19222             if(this.indicatorpos == 'right'){
19223                 cfg.cn = [
19224                     {
19225                         tag: 'label',
19226                         'for' :  id,
19227                         cls : 'control-label col-form-label',
19228                         cn : [
19229                             {
19230                                 tag : 'span',
19231                                 html : this.fieldLabel
19232                             },
19233                             required
19234                         ]
19235                     },
19236                     {
19237                         cls : "roo-combobox-wrap ",
19238                         cn: [
19239                             combobox
19240                         ]
19241                     }
19242
19243                 ];
19244                 
19245                 labelCfg = cfg.cn[0];
19246                 contentCfg = cfg.cn[1];
19247             }
19248             
19249            
19250             
19251             if(this.labelWidth > 12){
19252                 labelCfg.style = "width: " + this.labelWidth + 'px';
19253             }
19254            
19255             if(this.labelWidth < 13 && this.labelmd == 0){
19256                 this.labelmd = this.labelWidth;
19257             }
19258             
19259             if(this.labellg > 0){
19260                 labelCfg.cls += ' col-lg-' + this.labellg;
19261                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19262             }
19263             
19264             if(this.labelmd > 0){
19265                 labelCfg.cls += ' col-md-' + this.labelmd;
19266                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19267             }
19268             
19269             if(this.labelsm > 0){
19270                 labelCfg.cls += ' col-sm-' + this.labelsm;
19271                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19272             }
19273             
19274             if(this.labelxs > 0){
19275                 labelCfg.cls += ' col-xs-' + this.labelxs;
19276                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19277             }
19278                 
19279                 
19280         } else if ( this.fieldLabel.length) {
19281             cfg.cn = [
19282                required,
19283                 {
19284                     tag: 'label',
19285                     cls : 'control-label',
19286                     html : this.fieldLabel
19287
19288                 },
19289                 {
19290                     cls : '', 
19291                     cn: [
19292                         combobox
19293                     ]
19294                 }
19295             ];
19296             
19297             if(this.indicatorpos == 'right'){
19298                 cfg.cn = [
19299                     {
19300                         tag: 'label',
19301                         cls : 'control-label',
19302                         html : this.fieldLabel,
19303                         cn : [
19304                             required
19305                         ]
19306                     },
19307                     {
19308                         cls : '', 
19309                         cn: [
19310                             combobox
19311                         ]
19312                     }
19313                 ];
19314             }
19315         } else {
19316             cfg.cn = combobox;    
19317         }
19318         
19319         
19320         var settings = this;
19321         
19322         ['xs','sm','md','lg'].map(function(size){
19323             if (settings[size]) {
19324                 cfg.cls += ' col-' + size + '-' + settings[size];
19325             }
19326         });
19327         
19328         return cfg;
19329     },
19330     
19331     initTouchView : function()
19332     {
19333         this.renderTouchView();
19334         
19335         this.touchViewEl.on('scroll', function(){
19336             this.el.dom.scrollTop = 0;
19337         }, this);
19338         
19339         this.originalValue = this.getValue();
19340         
19341         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19342         
19343         this.inputEl().on("click", this.showTouchView, this);
19344         if (this.triggerEl) {
19345             this.triggerEl.on("click", this.showTouchView, this);
19346         }
19347         
19348         
19349         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19350         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19351         
19352         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19353         
19354         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19355         this.store.on('load', this.onTouchViewLoad, this);
19356         this.store.on('loadexception', this.onTouchViewLoadException, this);
19357         
19358         if(this.hiddenName){
19359             
19360             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19361             
19362             this.hiddenField.dom.value =
19363                 this.hiddenValue !== undefined ? this.hiddenValue :
19364                 this.value !== undefined ? this.value : '';
19365         
19366             this.el.dom.removeAttribute('name');
19367             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19368         }
19369         
19370         if(this.multiple){
19371             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19372             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19373         }
19374         
19375         if(this.removable && !this.multiple){
19376             var close = this.closeTriggerEl();
19377             if(close){
19378                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19379                 close.on('click', this.removeBtnClick, this, close);
19380             }
19381         }
19382         /*
19383          * fix the bug in Safari iOS8
19384          */
19385         this.inputEl().on("focus", function(e){
19386             document.activeElement.blur();
19387         }, this);
19388         
19389         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19390         
19391         return;
19392         
19393         
19394     },
19395     
19396     renderTouchView : function()
19397     {
19398         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19399         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19400         
19401         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19402         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19403         
19404         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19405         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19406         this.touchViewBodyEl.setStyle('overflow', 'auto');
19407         
19408         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19409         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19410         
19411         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19412         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19413         
19414     },
19415     
19416     showTouchView : function()
19417     {
19418         if(this.disabled){
19419             return;
19420         }
19421         
19422         this.touchViewHeaderEl.hide();
19423
19424         if(this.modalTitle.length){
19425             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19426             this.touchViewHeaderEl.show();
19427         }
19428
19429         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19430         this.touchViewEl.show();
19431
19432         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19433         
19434         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19435         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19436
19437         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19438
19439         if(this.modalTitle.length){
19440             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19441         }
19442         
19443         this.touchViewBodyEl.setHeight(bodyHeight);
19444
19445         if(this.animate){
19446             var _this = this;
19447             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19448         }else{
19449             this.touchViewEl.addClass(['in','show']);
19450         }
19451         
19452         if(this._touchViewMask){
19453             Roo.get(document.body).addClass("x-body-masked");
19454             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19455             this._touchViewMask.setStyle('z-index', 10000);
19456             this._touchViewMask.addClass('show');
19457         }
19458         
19459         this.doTouchViewQuery();
19460         
19461     },
19462     
19463     hideTouchView : function()
19464     {
19465         this.touchViewEl.removeClass(['in','show']);
19466
19467         if(this.animate){
19468             var _this = this;
19469             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19470         }else{
19471             this.touchViewEl.setStyle('display', 'none');
19472         }
19473         
19474         if(this._touchViewMask){
19475             this._touchViewMask.removeClass('show');
19476             Roo.get(document.body).removeClass("x-body-masked");
19477         }
19478     },
19479     
19480     setTouchViewValue : function()
19481     {
19482         if(this.multiple){
19483             this.clearItem();
19484         
19485             var _this = this;
19486
19487             Roo.each(this.tickItems, function(o){
19488                 this.addItem(o);
19489             }, this);
19490         }
19491         
19492         this.hideTouchView();
19493     },
19494     
19495     doTouchViewQuery : function()
19496     {
19497         var qe = {
19498             query: '',
19499             forceAll: true,
19500             combo: this,
19501             cancel:false
19502         };
19503         
19504         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19505             return false;
19506         }
19507         
19508         if(!this.alwaysQuery || this.mode == 'local'){
19509             this.onTouchViewLoad();
19510             return;
19511         }
19512         
19513         this.store.load();
19514     },
19515     
19516     onTouchViewBeforeLoad : function(combo,opts)
19517     {
19518         return;
19519     },
19520
19521     // private
19522     onTouchViewLoad : function()
19523     {
19524         if(this.store.getCount() < 1){
19525             this.onTouchViewEmptyResults();
19526             return;
19527         }
19528         
19529         this.clearTouchView();
19530         
19531         var rawValue = this.getRawValue();
19532         
19533         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19534         
19535         this.tickItems = [];
19536         
19537         this.store.data.each(function(d, rowIndex){
19538             var row = this.touchViewListGroup.createChild(template);
19539             
19540             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19541                 row.addClass(d.data.cls);
19542             }
19543             
19544             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19545                 var cfg = {
19546                     data : d.data,
19547                     html : d.data[this.displayField]
19548                 };
19549                 
19550                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19551                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19552                 }
19553             }
19554             row.removeClass('selected');
19555             if(!this.multiple && this.valueField &&
19556                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19557             {
19558                 // radio buttons..
19559                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19560                 row.addClass('selected');
19561             }
19562             
19563             if(this.multiple && this.valueField &&
19564                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19565             {
19566                 
19567                 // checkboxes...
19568                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19569                 this.tickItems.push(d.data);
19570             }
19571             
19572             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19573             
19574         }, this);
19575         
19576         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19577         
19578         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19579
19580         if(this.modalTitle.length){
19581             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19582         }
19583
19584         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19585         
19586         if(this.mobile_restrict_height && listHeight < bodyHeight){
19587             this.touchViewBodyEl.setHeight(listHeight);
19588         }
19589         
19590         var _this = this;
19591         
19592         if(firstChecked && listHeight > bodyHeight){
19593             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19594         }
19595         
19596     },
19597     
19598     onTouchViewLoadException : function()
19599     {
19600         this.hideTouchView();
19601     },
19602     
19603     onTouchViewEmptyResults : function()
19604     {
19605         this.clearTouchView();
19606         
19607         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19608         
19609         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19610         
19611     },
19612     
19613     clearTouchView : function()
19614     {
19615         this.touchViewListGroup.dom.innerHTML = '';
19616     },
19617     
19618     onTouchViewClick : function(e, el, o)
19619     {
19620         e.preventDefault();
19621         
19622         var row = o.row;
19623         var rowIndex = o.rowIndex;
19624         
19625         var r = this.store.getAt(rowIndex);
19626         
19627         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19628             
19629             if(!this.multiple){
19630                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19631                     c.dom.removeAttribute('checked');
19632                 }, this);
19633
19634                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19635
19636                 this.setFromData(r.data);
19637
19638                 var close = this.closeTriggerEl();
19639
19640                 if(close){
19641                     close.show();
19642                 }
19643
19644                 this.hideTouchView();
19645
19646                 this.fireEvent('select', this, r, rowIndex);
19647
19648                 return;
19649             }
19650
19651             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19652                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19653                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19654                 return;
19655             }
19656
19657             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19658             this.addItem(r.data);
19659             this.tickItems.push(r.data);
19660         }
19661     },
19662     
19663     getAutoCreateNativeIOS : function()
19664     {
19665         var cfg = {
19666             cls: 'form-group' //input-group,
19667         };
19668         
19669         var combobox =  {
19670             tag: 'select',
19671             cls : 'roo-ios-select'
19672         };
19673         
19674         if (this.name) {
19675             combobox.name = this.name;
19676         }
19677         
19678         if (this.disabled) {
19679             combobox.disabled = true;
19680         }
19681         
19682         var settings = this;
19683         
19684         ['xs','sm','md','lg'].map(function(size){
19685             if (settings[size]) {
19686                 cfg.cls += ' col-' + size + '-' + settings[size];
19687             }
19688         });
19689         
19690         cfg.cn = combobox;
19691         
19692         return cfg;
19693         
19694     },
19695     
19696     initIOSView : function()
19697     {
19698         this.store.on('load', this.onIOSViewLoad, this);
19699         
19700         return;
19701     },
19702     
19703     onIOSViewLoad : function()
19704     {
19705         if(this.store.getCount() < 1){
19706             return;
19707         }
19708         
19709         this.clearIOSView();
19710         
19711         if(this.allowBlank) {
19712             
19713             var default_text = '-- SELECT --';
19714             
19715             if(this.placeholder.length){
19716                 default_text = this.placeholder;
19717             }
19718             
19719             if(this.emptyTitle.length){
19720                 default_text += ' - ' + this.emptyTitle + ' -';
19721             }
19722             
19723             var opt = this.inputEl().createChild({
19724                 tag: 'option',
19725                 value : 0,
19726                 html : default_text
19727             });
19728             
19729             var o = {};
19730             o[this.valueField] = 0;
19731             o[this.displayField] = default_text;
19732             
19733             this.ios_options.push({
19734                 data : o,
19735                 el : opt
19736             });
19737             
19738         }
19739         
19740         this.store.data.each(function(d, rowIndex){
19741             
19742             var html = '';
19743             
19744             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19745                 html = d.data[this.displayField];
19746             }
19747             
19748             var value = '';
19749             
19750             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19751                 value = d.data[this.valueField];
19752             }
19753             
19754             var option = {
19755                 tag: 'option',
19756                 value : value,
19757                 html : html
19758             };
19759             
19760             if(this.value == d.data[this.valueField]){
19761                 option['selected'] = true;
19762             }
19763             
19764             var opt = this.inputEl().createChild(option);
19765             
19766             this.ios_options.push({
19767                 data : d.data,
19768                 el : opt
19769             });
19770             
19771         }, this);
19772         
19773         this.inputEl().on('change', function(){
19774            this.fireEvent('select', this);
19775         }, this);
19776         
19777     },
19778     
19779     clearIOSView: function()
19780     {
19781         this.inputEl().dom.innerHTML = '';
19782         
19783         this.ios_options = [];
19784     },
19785     
19786     setIOSValue: function(v)
19787     {
19788         this.value = v;
19789         
19790         if(!this.ios_options){
19791             return;
19792         }
19793         
19794         Roo.each(this.ios_options, function(opts){
19795            
19796            opts.el.dom.removeAttribute('selected');
19797            
19798            if(opts.data[this.valueField] != v){
19799                return;
19800            }
19801            
19802            opts.el.dom.setAttribute('selected', true);
19803            
19804         }, this);
19805     }
19806
19807     /** 
19808     * @cfg {Boolean} grow 
19809     * @hide 
19810     */
19811     /** 
19812     * @cfg {Number} growMin 
19813     * @hide 
19814     */
19815     /** 
19816     * @cfg {Number} growMax 
19817     * @hide 
19818     */
19819     /**
19820      * @hide
19821      * @method autoSize
19822      */
19823 });
19824
19825 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19826     
19827     header : {
19828         tag: 'div',
19829         cls: 'modal-header',
19830         cn: [
19831             {
19832                 tag: 'h4',
19833                 cls: 'modal-title'
19834             }
19835         ]
19836     },
19837     
19838     body : {
19839         tag: 'div',
19840         cls: 'modal-body',
19841         cn: [
19842             {
19843                 tag: 'ul',
19844                 cls: 'list-group'
19845             }
19846         ]
19847     },
19848     
19849     listItemRadio : {
19850         tag: 'li',
19851         cls: 'list-group-item',
19852         cn: [
19853             {
19854                 tag: 'span',
19855                 cls: 'roo-combobox-list-group-item-value'
19856             },
19857             {
19858                 tag: 'div',
19859                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19860                 cn: [
19861                     {
19862                         tag: 'input',
19863                         type: 'radio'
19864                     },
19865                     {
19866                         tag: 'label'
19867                     }
19868                 ]
19869             }
19870         ]
19871     },
19872     
19873     listItemCheckbox : {
19874         tag: 'li',
19875         cls: 'list-group-item',
19876         cn: [
19877             {
19878                 tag: 'span',
19879                 cls: 'roo-combobox-list-group-item-value'
19880             },
19881             {
19882                 tag: 'div',
19883                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19884                 cn: [
19885                     {
19886                         tag: 'input',
19887                         type: 'checkbox'
19888                     },
19889                     {
19890                         tag: 'label'
19891                     }
19892                 ]
19893             }
19894         ]
19895     },
19896     
19897     emptyResult : {
19898         tag: 'div',
19899         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19900     },
19901     
19902     footer : {
19903         tag: 'div',
19904         cls: 'modal-footer',
19905         cn: [
19906             {
19907                 tag: 'div',
19908                 cls: 'row',
19909                 cn: [
19910                     {
19911                         tag: 'div',
19912                         cls: 'col-xs-6 text-left',
19913                         cn: {
19914                             tag: 'button',
19915                             cls: 'btn btn-danger roo-touch-view-cancel',
19916                             html: 'Cancel'
19917                         }
19918                     },
19919                     {
19920                         tag: 'div',
19921                         cls: 'col-xs-6 text-right',
19922                         cn: {
19923                             tag: 'button',
19924                             cls: 'btn btn-success roo-touch-view-ok',
19925                             html: 'OK'
19926                         }
19927                     }
19928                 ]
19929             }
19930         ]
19931         
19932     }
19933 });
19934
19935 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19936     
19937     touchViewTemplate : {
19938         tag: 'div',
19939         cls: 'modal fade roo-combobox-touch-view',
19940         cn: [
19941             {
19942                 tag: 'div',
19943                 cls: 'modal-dialog',
19944                 style : 'position:fixed', // we have to fix position....
19945                 cn: [
19946                     {
19947                         tag: 'div',
19948                         cls: 'modal-content',
19949                         cn: [
19950                             Roo.bootstrap.form.ComboBox.header,
19951                             Roo.bootstrap.form.ComboBox.body,
19952                             Roo.bootstrap.form.ComboBox.footer
19953                         ]
19954                     }
19955                 ]
19956             }
19957         ]
19958     }
19959 });/*
19960  * Based on:
19961  * Ext JS Library 1.1.1
19962  * Copyright(c) 2006-2007, Ext JS, LLC.
19963  *
19964  * Originally Released Under LGPL - original licence link has changed is not relivant.
19965  *
19966  * Fork - LGPL
19967  * <script type="text/javascript">
19968  */
19969
19970 /**
19971  * @class Roo.View
19972  * @extends Roo.util.Observable
19973  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19974  * This class also supports single and multi selection modes. <br>
19975  * Create a data model bound view:
19976  <pre><code>
19977  var store = new Roo.data.Store(...);
19978
19979  var view = new Roo.View({
19980     el : "my-element",
19981     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19982  
19983     singleSelect: true,
19984     selectedClass: "ydataview-selected",
19985     store: store
19986  });
19987
19988  // listen for node click?
19989  view.on("click", function(vw, index, node, e){
19990  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19991  });
19992
19993  // load XML data
19994  dataModel.load("foobar.xml");
19995  </code></pre>
19996  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19997  * <br><br>
19998  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19999  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
20000  * 
20001  * Note: old style constructor is still suported (container, template, config)
20002  * 
20003  * @constructor
20004  * Create a new View
20005  * @param {Object} config The config object
20006  * 
20007  */
20008 Roo.View = function(config, depreciated_tpl, depreciated_config){
20009     
20010     this.parent = false;
20011     
20012     if (typeof(depreciated_tpl) == 'undefined') {
20013         // new way.. - universal constructor.
20014         Roo.apply(this, config);
20015         this.el  = Roo.get(this.el);
20016     } else {
20017         // old format..
20018         this.el  = Roo.get(config);
20019         this.tpl = depreciated_tpl;
20020         Roo.apply(this, depreciated_config);
20021     }
20022     this.wrapEl  = this.el.wrap().wrap();
20023     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
20024     
20025     
20026     if(typeof(this.tpl) == "string"){
20027         this.tpl = new Roo.Template(this.tpl);
20028     } else {
20029         // support xtype ctors..
20030         this.tpl = new Roo.factory(this.tpl, Roo);
20031     }
20032     
20033     
20034     this.tpl.compile();
20035     
20036     /** @private */
20037     this.addEvents({
20038         /**
20039          * @event beforeclick
20040          * Fires before a click is processed. Returns false to cancel the default action.
20041          * @param {Roo.View} this
20042          * @param {Number} index The index of the target node
20043          * @param {HTMLElement} node The target node
20044          * @param {Roo.EventObject} e The raw event object
20045          */
20046             "beforeclick" : true,
20047         /**
20048          * @event click
20049          * Fires when a template node is clicked.
20050          * @param {Roo.View} this
20051          * @param {Number} index The index of the target node
20052          * @param {HTMLElement} node The target node
20053          * @param {Roo.EventObject} e The raw event object
20054          */
20055             "click" : true,
20056         /**
20057          * @event dblclick
20058          * Fires when a template node is double clicked.
20059          * @param {Roo.View} this
20060          * @param {Number} index The index of the target node
20061          * @param {HTMLElement} node The target node
20062          * @param {Roo.EventObject} e The raw event object
20063          */
20064             "dblclick" : true,
20065         /**
20066          * @event contextmenu
20067          * Fires when a template node is right clicked.
20068          * @param {Roo.View} this
20069          * @param {Number} index The index of the target node
20070          * @param {HTMLElement} node The target node
20071          * @param {Roo.EventObject} e The raw event object
20072          */
20073             "contextmenu" : true,
20074         /**
20075          * @event selectionchange
20076          * Fires when the selected nodes change.
20077          * @param {Roo.View} this
20078          * @param {Array} selections Array of the selected nodes
20079          */
20080             "selectionchange" : true,
20081     
20082         /**
20083          * @event beforeselect
20084          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20085          * @param {Roo.View} this
20086          * @param {HTMLElement} node The node to be selected
20087          * @param {Array} selections Array of currently selected nodes
20088          */
20089             "beforeselect" : true,
20090         /**
20091          * @event preparedata
20092          * Fires on every row to render, to allow you to change the data.
20093          * @param {Roo.View} this
20094          * @param {Object} data to be rendered (change this)
20095          */
20096           "preparedata" : true
20097           
20098           
20099         });
20100
20101
20102
20103     this.el.on({
20104         "click": this.onClick,
20105         "dblclick": this.onDblClick,
20106         "contextmenu": this.onContextMenu,
20107         scope:this
20108     });
20109
20110     this.selections = [];
20111     this.nodes = [];
20112     this.cmp = new Roo.CompositeElementLite([]);
20113     if(this.store){
20114         this.store = Roo.factory(this.store, Roo.data);
20115         this.setStore(this.store, true);
20116     }
20117     
20118     if ( this.footer && this.footer.xtype) {
20119            
20120          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20121         
20122         this.footer.dataSource = this.store;
20123         this.footer.container = fctr;
20124         this.footer = Roo.factory(this.footer, Roo);
20125         fctr.insertFirst(this.el);
20126         
20127         // this is a bit insane - as the paging toolbar seems to detach the el..
20128 //        dom.parentNode.parentNode.parentNode
20129          // they get detached?
20130     }
20131     
20132     
20133     Roo.View.superclass.constructor.call(this);
20134     
20135     
20136 };
20137
20138 Roo.extend(Roo.View, Roo.util.Observable, {
20139     
20140      /**
20141      * @cfg {Roo.data.Store} store Data store to load data from.
20142      */
20143     store : false,
20144     
20145     /**
20146      * @cfg {String|Roo.Element} el The container element.
20147      */
20148     el : '',
20149     
20150     /**
20151      * @cfg {String|Roo.Template} tpl The template used by this View 
20152      */
20153     tpl : false,
20154     /**
20155      * @cfg {String} dataName the named area of the template to use as the data area
20156      *                          Works with domtemplates roo-name="name"
20157      */
20158     dataName: false,
20159     /**
20160      * @cfg {String} selectedClass The css class to add to selected nodes
20161      */
20162     selectedClass : "x-view-selected",
20163      /**
20164      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20165      */
20166     emptyText : "",
20167     
20168     /**
20169      * @cfg {String} text to display on mask (default Loading)
20170      */
20171     mask : false,
20172     /**
20173      * @cfg {Boolean} multiSelect Allow multiple selection
20174      */
20175     multiSelect : false,
20176     /**
20177      * @cfg {Boolean} singleSelect Allow single selection
20178      */
20179     singleSelect:  false,
20180     
20181     /**
20182      * @cfg {Boolean} toggleSelect - selecting 
20183      */
20184     toggleSelect : false,
20185     
20186     /**
20187      * @cfg {Boolean} tickable - selecting 
20188      */
20189     tickable : false,
20190     
20191     /**
20192      * Returns the element this view is bound to.
20193      * @return {Roo.Element}
20194      */
20195     getEl : function(){
20196         return this.wrapEl;
20197     },
20198     
20199     
20200
20201     /**
20202      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20203      */
20204     refresh : function(){
20205         //Roo.log('refresh');
20206         var t = this.tpl;
20207         
20208         // if we are using something like 'domtemplate', then
20209         // the what gets used is:
20210         // t.applySubtemplate(NAME, data, wrapping data..)
20211         // the outer template then get' applied with
20212         //     the store 'extra data'
20213         // and the body get's added to the
20214         //      roo-name="data" node?
20215         //      <span class='roo-tpl-{name}'></span> ?????
20216         
20217         
20218         
20219         this.clearSelections();
20220         this.el.update("");
20221         var html = [];
20222         var records = this.store.getRange();
20223         if(records.length < 1) {
20224             
20225             // is this valid??  = should it render a template??
20226             
20227             this.el.update(this.emptyText);
20228             return;
20229         }
20230         var el = this.el;
20231         if (this.dataName) {
20232             this.el.update(t.apply(this.store.meta)); //????
20233             el = this.el.child('.roo-tpl-' + this.dataName);
20234         }
20235         
20236         for(var i = 0, len = records.length; i < len; i++){
20237             var data = this.prepareData(records[i].data, i, records[i]);
20238             this.fireEvent("preparedata", this, data, i, records[i]);
20239             
20240             var d = Roo.apply({}, data);
20241             
20242             if(this.tickable){
20243                 Roo.apply(d, {'roo-id' : Roo.id()});
20244                 
20245                 var _this = this;
20246             
20247                 Roo.each(this.parent.item, function(item){
20248                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20249                         return;
20250                     }
20251                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20252                 });
20253             }
20254             
20255             html[html.length] = Roo.util.Format.trim(
20256                 this.dataName ?
20257                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20258                     t.apply(d)
20259             );
20260         }
20261         
20262         
20263         
20264         el.update(html.join(""));
20265         this.nodes = el.dom.childNodes;
20266         this.updateIndexes(0);
20267     },
20268     
20269
20270     /**
20271      * Function to override to reformat the data that is sent to
20272      * the template for each node.
20273      * DEPRICATED - use the preparedata event handler.
20274      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20275      * a JSON object for an UpdateManager bound view).
20276      */
20277     prepareData : function(data, index, record)
20278     {
20279         this.fireEvent("preparedata", this, data, index, record);
20280         return data;
20281     },
20282
20283     onUpdate : function(ds, record){
20284         // Roo.log('on update');   
20285         this.clearSelections();
20286         var index = this.store.indexOf(record);
20287         var n = this.nodes[index];
20288         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20289         n.parentNode.removeChild(n);
20290         this.updateIndexes(index, index);
20291     },
20292
20293     
20294     
20295 // --------- FIXME     
20296     onAdd : function(ds, records, index)
20297     {
20298         //Roo.log(['on Add', ds, records, index] );        
20299         this.clearSelections();
20300         if(this.nodes.length == 0){
20301             this.refresh();
20302             return;
20303         }
20304         var n = this.nodes[index];
20305         for(var i = 0, len = records.length; i < len; i++){
20306             var d = this.prepareData(records[i].data, i, records[i]);
20307             if(n){
20308                 this.tpl.insertBefore(n, d);
20309             }else{
20310                 
20311                 this.tpl.append(this.el, d);
20312             }
20313         }
20314         this.updateIndexes(index);
20315     },
20316
20317     onRemove : function(ds, record, index){
20318        // Roo.log('onRemove');
20319         this.clearSelections();
20320         var el = this.dataName  ?
20321             this.el.child('.roo-tpl-' + this.dataName) :
20322             this.el; 
20323         
20324         el.dom.removeChild(this.nodes[index]);
20325         this.updateIndexes(index);
20326     },
20327
20328     /**
20329      * Refresh an individual node.
20330      * @param {Number} index
20331      */
20332     refreshNode : function(index){
20333         this.onUpdate(this.store, this.store.getAt(index));
20334     },
20335
20336     updateIndexes : function(startIndex, endIndex){
20337         var ns = this.nodes;
20338         startIndex = startIndex || 0;
20339         endIndex = endIndex || ns.length - 1;
20340         for(var i = startIndex; i <= endIndex; i++){
20341             ns[i].nodeIndex = i;
20342         }
20343     },
20344
20345     /**
20346      * Changes the data store this view uses and refresh the view.
20347      * @param {Store} store
20348      */
20349     setStore : function(store, initial){
20350         if(!initial && this.store){
20351             this.store.un("datachanged", this.refresh);
20352             this.store.un("add", this.onAdd);
20353             this.store.un("remove", this.onRemove);
20354             this.store.un("update", this.onUpdate);
20355             this.store.un("clear", this.refresh);
20356             this.store.un("beforeload", this.onBeforeLoad);
20357             this.store.un("load", this.onLoad);
20358             this.store.un("loadexception", this.onLoad);
20359         }
20360         if(store){
20361           
20362             store.on("datachanged", this.refresh, this);
20363             store.on("add", this.onAdd, this);
20364             store.on("remove", this.onRemove, this);
20365             store.on("update", this.onUpdate, this);
20366             store.on("clear", this.refresh, this);
20367             store.on("beforeload", this.onBeforeLoad, this);
20368             store.on("load", this.onLoad, this);
20369             store.on("loadexception", this.onLoad, this);
20370         }
20371         
20372         if(store){
20373             this.refresh();
20374         }
20375     },
20376     /**
20377      * onbeforeLoad - masks the loading area.
20378      *
20379      */
20380     onBeforeLoad : function(store,opts)
20381     {
20382          //Roo.log('onBeforeLoad');   
20383         if (!opts.add) {
20384             this.el.update("");
20385         }
20386         this.el.mask(this.mask ? this.mask : "Loading" ); 
20387     },
20388     onLoad : function ()
20389     {
20390         this.el.unmask();
20391     },
20392     
20393
20394     /**
20395      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20396      * @param {HTMLElement} node
20397      * @return {HTMLElement} The template node
20398      */
20399     findItemFromChild : function(node){
20400         var el = this.dataName  ?
20401             this.el.child('.roo-tpl-' + this.dataName,true) :
20402             this.el.dom; 
20403         
20404         if(!node || node.parentNode == el){
20405                     return node;
20406             }
20407             var p = node.parentNode;
20408             while(p && p != el){
20409             if(p.parentNode == el){
20410                 return p;
20411             }
20412             p = p.parentNode;
20413         }
20414             return null;
20415     },
20416
20417     /** @ignore */
20418     onClick : function(e){
20419         var item = this.findItemFromChild(e.getTarget());
20420         if(item){
20421             var index = this.indexOf(item);
20422             if(this.onItemClick(item, index, e) !== false){
20423                 this.fireEvent("click", this, index, item, e);
20424             }
20425         }else{
20426             this.clearSelections();
20427         }
20428     },
20429
20430     /** @ignore */
20431     onContextMenu : function(e){
20432         var item = this.findItemFromChild(e.getTarget());
20433         if(item){
20434             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20435         }
20436     },
20437
20438     /** @ignore */
20439     onDblClick : function(e){
20440         var item = this.findItemFromChild(e.getTarget());
20441         if(item){
20442             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20443         }
20444     },
20445
20446     onItemClick : function(item, index, e)
20447     {
20448         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20449             return false;
20450         }
20451         if (this.toggleSelect) {
20452             var m = this.isSelected(item) ? 'unselect' : 'select';
20453             //Roo.log(m);
20454             var _t = this;
20455             _t[m](item, true, false);
20456             return true;
20457         }
20458         if(this.multiSelect || this.singleSelect){
20459             if(this.multiSelect && e.shiftKey && this.lastSelection){
20460                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20461             }else{
20462                 this.select(item, this.multiSelect && e.ctrlKey);
20463                 this.lastSelection = item;
20464             }
20465             
20466             if(!this.tickable){
20467                 e.preventDefault();
20468             }
20469             
20470         }
20471         return true;
20472     },
20473
20474     /**
20475      * Get the number of selected nodes.
20476      * @return {Number}
20477      */
20478     getSelectionCount : function(){
20479         return this.selections.length;
20480     },
20481
20482     /**
20483      * Get the currently selected nodes.
20484      * @return {Array} An array of HTMLElements
20485      */
20486     getSelectedNodes : function(){
20487         return this.selections;
20488     },
20489
20490     /**
20491      * Get the indexes of the selected nodes.
20492      * @return {Array}
20493      */
20494     getSelectedIndexes : function(){
20495         var indexes = [], s = this.selections;
20496         for(var i = 0, len = s.length; i < len; i++){
20497             indexes.push(s[i].nodeIndex);
20498         }
20499         return indexes;
20500     },
20501
20502     /**
20503      * Clear all selections
20504      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20505      */
20506     clearSelections : function(suppressEvent){
20507         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20508             this.cmp.elements = this.selections;
20509             this.cmp.removeClass(this.selectedClass);
20510             this.selections = [];
20511             if(!suppressEvent){
20512                 this.fireEvent("selectionchange", this, this.selections);
20513             }
20514         }
20515     },
20516
20517     /**
20518      * Returns true if the passed node is selected
20519      * @param {HTMLElement/Number} node The node or node index
20520      * @return {Boolean}
20521      */
20522     isSelected : function(node){
20523         var s = this.selections;
20524         if(s.length < 1){
20525             return false;
20526         }
20527         node = this.getNode(node);
20528         return s.indexOf(node) !== -1;
20529     },
20530
20531     /**
20532      * Selects nodes.
20533      * @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
20534      * @param {Boolean} keepExisting (optional) true to keep existing selections
20535      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20536      */
20537     select : function(nodeInfo, keepExisting, suppressEvent){
20538         if(nodeInfo instanceof Array){
20539             if(!keepExisting){
20540                 this.clearSelections(true);
20541             }
20542             for(var i = 0, len = nodeInfo.length; i < len; i++){
20543                 this.select(nodeInfo[i], true, true);
20544             }
20545             return;
20546         } 
20547         var node = this.getNode(nodeInfo);
20548         if(!node || this.isSelected(node)){
20549             return; // already selected.
20550         }
20551         if(!keepExisting){
20552             this.clearSelections(true);
20553         }
20554         
20555         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20556             Roo.fly(node).addClass(this.selectedClass);
20557             this.selections.push(node);
20558             if(!suppressEvent){
20559                 this.fireEvent("selectionchange", this, this.selections);
20560             }
20561         }
20562         
20563         
20564     },
20565       /**
20566      * Unselects nodes.
20567      * @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
20568      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20569      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20570      */
20571     unselect : function(nodeInfo, keepExisting, suppressEvent)
20572     {
20573         if(nodeInfo instanceof Array){
20574             Roo.each(this.selections, function(s) {
20575                 this.unselect(s, nodeInfo);
20576             }, this);
20577             return;
20578         }
20579         var node = this.getNode(nodeInfo);
20580         if(!node || !this.isSelected(node)){
20581             //Roo.log("not selected");
20582             return; // not selected.
20583         }
20584         // fireevent???
20585         var ns = [];
20586         Roo.each(this.selections, function(s) {
20587             if (s == node ) {
20588                 Roo.fly(node).removeClass(this.selectedClass);
20589
20590                 return;
20591             }
20592             ns.push(s);
20593         },this);
20594         
20595         this.selections= ns;
20596         this.fireEvent("selectionchange", this, this.selections);
20597     },
20598
20599     /**
20600      * Gets a template node.
20601      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20602      * @return {HTMLElement} The node or null if it wasn't found
20603      */
20604     getNode : function(nodeInfo){
20605         if(typeof nodeInfo == "string"){
20606             return document.getElementById(nodeInfo);
20607         }else if(typeof nodeInfo == "number"){
20608             return this.nodes[nodeInfo];
20609         }
20610         return nodeInfo;
20611     },
20612
20613     /**
20614      * Gets a range template nodes.
20615      * @param {Number} startIndex
20616      * @param {Number} endIndex
20617      * @return {Array} An array of nodes
20618      */
20619     getNodes : function(start, end){
20620         var ns = this.nodes;
20621         start = start || 0;
20622         end = typeof end == "undefined" ? ns.length - 1 : end;
20623         var nodes = [];
20624         if(start <= end){
20625             for(var i = start; i <= end; i++){
20626                 nodes.push(ns[i]);
20627             }
20628         } else{
20629             for(var i = start; i >= end; i--){
20630                 nodes.push(ns[i]);
20631             }
20632         }
20633         return nodes;
20634     },
20635
20636     /**
20637      * Finds the index of the passed node
20638      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20639      * @return {Number} The index of the node or -1
20640      */
20641     indexOf : function(node){
20642         node = this.getNode(node);
20643         if(typeof node.nodeIndex == "number"){
20644             return node.nodeIndex;
20645         }
20646         var ns = this.nodes;
20647         for(var i = 0, len = ns.length; i < len; i++){
20648             if(ns[i] == node){
20649                 return i;
20650             }
20651         }
20652         return -1;
20653     }
20654 });
20655 /*
20656  * - LGPL
20657  *
20658  * based on jquery fullcalendar
20659  * 
20660  */
20661
20662 Roo.bootstrap = Roo.bootstrap || {};
20663 /**
20664  * @class Roo.bootstrap.Calendar
20665  * @extends Roo.bootstrap.Component
20666  * Bootstrap Calendar class
20667  * @cfg {Boolean} loadMask (true|false) default false
20668  * @cfg {Object} header generate the user specific header of the calendar, default false
20669
20670  * @constructor
20671  * Create a new Container
20672  * @param {Object} config The config object
20673  */
20674
20675
20676
20677 Roo.bootstrap.Calendar = function(config){
20678     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20679      this.addEvents({
20680         /**
20681              * @event select
20682              * Fires when a date is selected
20683              * @param {DatePicker} this
20684              * @param {Date} date The selected date
20685              */
20686         'select': true,
20687         /**
20688              * @event monthchange
20689              * Fires when the displayed month changes 
20690              * @param {DatePicker} this
20691              * @param {Date} date The selected month
20692              */
20693         'monthchange': true,
20694         /**
20695              * @event evententer
20696              * Fires when mouse over an event
20697              * @param {Calendar} this
20698              * @param {event} Event
20699              */
20700         'evententer': true,
20701         /**
20702              * @event eventleave
20703              * Fires when the mouse leaves an
20704              * @param {Calendar} this
20705              * @param {event}
20706              */
20707         'eventleave': true,
20708         /**
20709              * @event eventclick
20710              * Fires when the mouse click an
20711              * @param {Calendar} this
20712              * @param {event}
20713              */
20714         'eventclick': true
20715         
20716     });
20717
20718 };
20719
20720 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20721     
20722           /**
20723      * @cfg {Roo.data.Store} store
20724      * The data source for the calendar
20725      */
20726         store : false,
20727      /**
20728      * @cfg {Number} startDay
20729      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20730      */
20731     startDay : 0,
20732     
20733     loadMask : false,
20734     
20735     header : false,
20736       
20737     getAutoCreate : function(){
20738         
20739         
20740         var fc_button = function(name, corner, style, content ) {
20741             return Roo.apply({},{
20742                 tag : 'span',
20743                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20744                          (corner.length ?
20745                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20746                             ''
20747                         ),
20748                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20749                 unselectable: 'on'
20750             });
20751         };
20752         
20753         var header = {};
20754         
20755         if(!this.header){
20756             header = {
20757                 tag : 'table',
20758                 cls : 'fc-header',
20759                 style : 'width:100%',
20760                 cn : [
20761                     {
20762                         tag: 'tr',
20763                         cn : [
20764                             {
20765                                 tag : 'td',
20766                                 cls : 'fc-header-left',
20767                                 cn : [
20768                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20769                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20770                                     { tag: 'span', cls: 'fc-header-space' },
20771                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20772
20773
20774                                 ]
20775                             },
20776
20777                             {
20778                                 tag : 'td',
20779                                 cls : 'fc-header-center',
20780                                 cn : [
20781                                     {
20782                                         tag: 'span',
20783                                         cls: 'fc-header-title',
20784                                         cn : {
20785                                             tag: 'H2',
20786                                             html : 'month / year'
20787                                         }
20788                                     }
20789
20790                                 ]
20791                             },
20792                             {
20793                                 tag : 'td',
20794                                 cls : 'fc-header-right',
20795                                 cn : [
20796                               /*      fc_button('month', 'left', '', 'month' ),
20797                                     fc_button('week', '', '', 'week' ),
20798                                     fc_button('day', 'right', '', 'day' )
20799                                 */    
20800
20801                                 ]
20802                             }
20803
20804                         ]
20805                     }
20806                 ]
20807             };
20808         }
20809         
20810         header = this.header;
20811         
20812        
20813         var cal_heads = function() {
20814             var ret = [];
20815             // fixme - handle this.
20816             
20817             for (var i =0; i < Date.dayNames.length; i++) {
20818                 var d = Date.dayNames[i];
20819                 ret.push({
20820                     tag: 'th',
20821                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20822                     html : d.substring(0,3)
20823                 });
20824                 
20825             }
20826             ret[0].cls += ' fc-first';
20827             ret[6].cls += ' fc-last';
20828             return ret;
20829         };
20830         var cal_cell = function(n) {
20831             return  {
20832                 tag: 'td',
20833                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20834                 cn : [
20835                     {
20836                         cn : [
20837                             {
20838                                 cls: 'fc-day-number',
20839                                 html: 'D'
20840                             },
20841                             {
20842                                 cls: 'fc-day-content',
20843                              
20844                                 cn : [
20845                                      {
20846                                         style: 'position: relative;' // height: 17px;
20847                                     }
20848                                 ]
20849                             }
20850                             
20851                             
20852                         ]
20853                     }
20854                 ]
20855                 
20856             }
20857         };
20858         var cal_rows = function() {
20859             
20860             var ret = [];
20861             for (var r = 0; r < 6; r++) {
20862                 var row= {
20863                     tag : 'tr',
20864                     cls : 'fc-week',
20865                     cn : []
20866                 };
20867                 
20868                 for (var i =0; i < Date.dayNames.length; i++) {
20869                     var d = Date.dayNames[i];
20870                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20871
20872                 }
20873                 row.cn[0].cls+=' fc-first';
20874                 row.cn[0].cn[0].style = 'min-height:90px';
20875                 row.cn[6].cls+=' fc-last';
20876                 ret.push(row);
20877                 
20878             }
20879             ret[0].cls += ' fc-first';
20880             ret[4].cls += ' fc-prev-last';
20881             ret[5].cls += ' fc-last';
20882             return ret;
20883             
20884         };
20885         
20886         var cal_table = {
20887             tag: 'table',
20888             cls: 'fc-border-separate',
20889             style : 'width:100%',
20890             cellspacing  : 0,
20891             cn : [
20892                 { 
20893                     tag: 'thead',
20894                     cn : [
20895                         { 
20896                             tag: 'tr',
20897                             cls : 'fc-first fc-last',
20898                             cn : cal_heads()
20899                         }
20900                     ]
20901                 },
20902                 { 
20903                     tag: 'tbody',
20904                     cn : cal_rows()
20905                 }
20906                   
20907             ]
20908         };
20909          
20910          var cfg = {
20911             cls : 'fc fc-ltr',
20912             cn : [
20913                 header,
20914                 {
20915                     cls : 'fc-content',
20916                     style : "position: relative;",
20917                     cn : [
20918                         {
20919                             cls : 'fc-view fc-view-month fc-grid',
20920                             style : 'position: relative',
20921                             unselectable : 'on',
20922                             cn : [
20923                                 {
20924                                     cls : 'fc-event-container',
20925                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20926                                 },
20927                                 cal_table
20928                             ]
20929                         }
20930                     ]
20931     
20932                 }
20933            ] 
20934             
20935         };
20936         
20937          
20938         
20939         return cfg;
20940     },
20941     
20942     
20943     initEvents : function()
20944     {
20945         if(!this.store){
20946             throw "can not find store for calendar";
20947         }
20948         
20949         var mark = {
20950             tag: "div",
20951             cls:"x-dlg-mask",
20952             style: "text-align:center",
20953             cn: [
20954                 {
20955                     tag: "div",
20956                     style: "background-color:white;width:50%;margin:250 auto",
20957                     cn: [
20958                         {
20959                             tag: "img",
20960                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20961                         },
20962                         {
20963                             tag: "span",
20964                             html: "Loading"
20965                         }
20966                         
20967                     ]
20968                 }
20969             ]
20970         };
20971         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20972         
20973         var size = this.el.select('.fc-content', true).first().getSize();
20974         this.maskEl.setSize(size.width, size.height);
20975         this.maskEl.enableDisplayMode("block");
20976         if(!this.loadMask){
20977             this.maskEl.hide();
20978         }
20979         
20980         this.store = Roo.factory(this.store, Roo.data);
20981         this.store.on('load', this.onLoad, this);
20982         this.store.on('beforeload', this.onBeforeLoad, this);
20983         
20984         this.resize();
20985         
20986         this.cells = this.el.select('.fc-day',true);
20987         //Roo.log(this.cells);
20988         this.textNodes = this.el.query('.fc-day-number');
20989         this.cells.addClassOnOver('fc-state-hover');
20990         
20991         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20992         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20993         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20994         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20995         
20996         this.on('monthchange', this.onMonthChange, this);
20997         
20998         this.update(new Date().clearTime());
20999     },
21000     
21001     resize : function() {
21002         var sz  = this.el.getSize();
21003         
21004         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
21005         this.el.select('.fc-day-content div',true).setHeight(34);
21006     },
21007     
21008     
21009     // private
21010     showPrevMonth : function(e){
21011         this.update(this.activeDate.add("mo", -1));
21012     },
21013     showToday : function(e){
21014         this.update(new Date().clearTime());
21015     },
21016     // private
21017     showNextMonth : function(e){
21018         this.update(this.activeDate.add("mo", 1));
21019     },
21020
21021     // private
21022     showPrevYear : function(){
21023         this.update(this.activeDate.add("y", -1));
21024     },
21025
21026     // private
21027     showNextYear : function(){
21028         this.update(this.activeDate.add("y", 1));
21029     },
21030
21031     
21032    // private
21033     update : function(date)
21034     {
21035         var vd = this.activeDate;
21036         this.activeDate = date;
21037 //        if(vd && this.el){
21038 //            var t = date.getTime();
21039 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21040 //                Roo.log('using add remove');
21041 //                
21042 //                this.fireEvent('monthchange', this, date);
21043 //                
21044 //                this.cells.removeClass("fc-state-highlight");
21045 //                this.cells.each(function(c){
21046 //                   if(c.dateValue == t){
21047 //                       c.addClass("fc-state-highlight");
21048 //                       setTimeout(function(){
21049 //                            try{c.dom.firstChild.focus();}catch(e){}
21050 //                       }, 50);
21051 //                       return false;
21052 //                   }
21053 //                   return true;
21054 //                });
21055 //                return;
21056 //            }
21057 //        }
21058         
21059         var days = date.getDaysInMonth();
21060         
21061         var firstOfMonth = date.getFirstDateOfMonth();
21062         var startingPos = firstOfMonth.getDay()-this.startDay;
21063         
21064         if(startingPos < this.startDay){
21065             startingPos += 7;
21066         }
21067         
21068         var pm = date.add(Date.MONTH, -1);
21069         var prevStart = pm.getDaysInMonth()-startingPos;
21070 //        
21071         this.cells = this.el.select('.fc-day',true);
21072         this.textNodes = this.el.query('.fc-day-number');
21073         this.cells.addClassOnOver('fc-state-hover');
21074         
21075         var cells = this.cells.elements;
21076         var textEls = this.textNodes;
21077         
21078         Roo.each(cells, function(cell){
21079             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21080         });
21081         
21082         days += startingPos;
21083
21084         // convert everything to numbers so it's fast
21085         var day = 86400000;
21086         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21087         //Roo.log(d);
21088         //Roo.log(pm);
21089         //Roo.log(prevStart);
21090         
21091         var today = new Date().clearTime().getTime();
21092         var sel = date.clearTime().getTime();
21093         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21094         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21095         var ddMatch = this.disabledDatesRE;
21096         var ddText = this.disabledDatesText;
21097         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21098         var ddaysText = this.disabledDaysText;
21099         var format = this.format;
21100         
21101         var setCellClass = function(cal, cell){
21102             cell.row = 0;
21103             cell.events = [];
21104             cell.more = [];
21105             //Roo.log('set Cell Class');
21106             cell.title = "";
21107             var t = d.getTime();
21108             
21109             //Roo.log(d);
21110             
21111             cell.dateValue = t;
21112             if(t == today){
21113                 cell.className += " fc-today";
21114                 cell.className += " fc-state-highlight";
21115                 cell.title = cal.todayText;
21116             }
21117             if(t == sel){
21118                 // disable highlight in other month..
21119                 //cell.className += " fc-state-highlight";
21120                 
21121             }
21122             // disabling
21123             if(t < min) {
21124                 cell.className = " fc-state-disabled";
21125                 cell.title = cal.minText;
21126                 return;
21127             }
21128             if(t > max) {
21129                 cell.className = " fc-state-disabled";
21130                 cell.title = cal.maxText;
21131                 return;
21132             }
21133             if(ddays){
21134                 if(ddays.indexOf(d.getDay()) != -1){
21135                     cell.title = ddaysText;
21136                     cell.className = " fc-state-disabled";
21137                 }
21138             }
21139             if(ddMatch && format){
21140                 var fvalue = d.dateFormat(format);
21141                 if(ddMatch.test(fvalue)){
21142                     cell.title = ddText.replace("%0", fvalue);
21143                     cell.className = " fc-state-disabled";
21144                 }
21145             }
21146             
21147             if (!cell.initialClassName) {
21148                 cell.initialClassName = cell.dom.className;
21149             }
21150             
21151             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21152         };
21153
21154         var i = 0;
21155         
21156         for(; i < startingPos; i++) {
21157             textEls[i].innerHTML = (++prevStart);
21158             d.setDate(d.getDate()+1);
21159             
21160             cells[i].className = "fc-past fc-other-month";
21161             setCellClass(this, cells[i]);
21162         }
21163         
21164         var intDay = 0;
21165         
21166         for(; i < days; i++){
21167             intDay = i - startingPos + 1;
21168             textEls[i].innerHTML = (intDay);
21169             d.setDate(d.getDate()+1);
21170             
21171             cells[i].className = ''; // "x-date-active";
21172             setCellClass(this, cells[i]);
21173         }
21174         var extraDays = 0;
21175         
21176         for(; i < 42; i++) {
21177             textEls[i].innerHTML = (++extraDays);
21178             d.setDate(d.getDate()+1);
21179             
21180             cells[i].className = "fc-future fc-other-month";
21181             setCellClass(this, cells[i]);
21182         }
21183         
21184         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21185         
21186         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21187         
21188         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21189         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21190         
21191         if(totalRows != 6){
21192             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21193             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21194         }
21195         
21196         this.fireEvent('monthchange', this, date);
21197         
21198         
21199         /*
21200         if(!this.internalRender){
21201             var main = this.el.dom.firstChild;
21202             var w = main.offsetWidth;
21203             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21204             Roo.fly(main).setWidth(w);
21205             this.internalRender = true;
21206             // opera does not respect the auto grow header center column
21207             // then, after it gets a width opera refuses to recalculate
21208             // without a second pass
21209             if(Roo.isOpera && !this.secondPass){
21210                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21211                 this.secondPass = true;
21212                 this.update.defer(10, this, [date]);
21213             }
21214         }
21215         */
21216         
21217     },
21218     
21219     findCell : function(dt) {
21220         dt = dt.clearTime().getTime();
21221         var ret = false;
21222         this.cells.each(function(c){
21223             //Roo.log("check " +c.dateValue + '?=' + dt);
21224             if(c.dateValue == dt){
21225                 ret = c;
21226                 return false;
21227             }
21228             return true;
21229         });
21230         
21231         return ret;
21232     },
21233     
21234     findCells : function(ev) {
21235         var s = ev.start.clone().clearTime().getTime();
21236        // Roo.log(s);
21237         var e= ev.end.clone().clearTime().getTime();
21238        // Roo.log(e);
21239         var ret = [];
21240         this.cells.each(function(c){
21241              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21242             
21243             if(c.dateValue > e){
21244                 return ;
21245             }
21246             if(c.dateValue < s){
21247                 return ;
21248             }
21249             ret.push(c);
21250         });
21251         
21252         return ret;    
21253     },
21254     
21255 //    findBestRow: function(cells)
21256 //    {
21257 //        var ret = 0;
21258 //        
21259 //        for (var i =0 ; i < cells.length;i++) {
21260 //            ret  = Math.max(cells[i].rows || 0,ret);
21261 //        }
21262 //        return ret;
21263 //        
21264 //    },
21265     
21266     
21267     addItem : function(ev)
21268     {
21269         // look for vertical location slot in
21270         var cells = this.findCells(ev);
21271         
21272 //        ev.row = this.findBestRow(cells);
21273         
21274         // work out the location.
21275         
21276         var crow = false;
21277         var rows = [];
21278         for(var i =0; i < cells.length; i++) {
21279             
21280             cells[i].row = cells[0].row;
21281             
21282             if(i == 0){
21283                 cells[i].row = cells[i].row + 1;
21284             }
21285             
21286             if (!crow) {
21287                 crow = {
21288                     start : cells[i],
21289                     end :  cells[i]
21290                 };
21291                 continue;
21292             }
21293             if (crow.start.getY() == cells[i].getY()) {
21294                 // on same row.
21295                 crow.end = cells[i];
21296                 continue;
21297             }
21298             // different row.
21299             rows.push(crow);
21300             crow = {
21301                 start: cells[i],
21302                 end : cells[i]
21303             };
21304             
21305         }
21306         
21307         rows.push(crow);
21308         ev.els = [];
21309         ev.rows = rows;
21310         ev.cells = cells;
21311         
21312         cells[0].events.push(ev);
21313         
21314         this.calevents.push(ev);
21315     },
21316     
21317     clearEvents: function() {
21318         
21319         if(!this.calevents){
21320             return;
21321         }
21322         
21323         Roo.each(this.cells.elements, function(c){
21324             c.row = 0;
21325             c.events = [];
21326             c.more = [];
21327         });
21328         
21329         Roo.each(this.calevents, function(e) {
21330             Roo.each(e.els, function(el) {
21331                 el.un('mouseenter' ,this.onEventEnter, this);
21332                 el.un('mouseleave' ,this.onEventLeave, this);
21333                 el.remove();
21334             },this);
21335         },this);
21336         
21337         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21338             e.remove();
21339         });
21340         
21341     },
21342     
21343     renderEvents: function()
21344     {   
21345         var _this = this;
21346         
21347         this.cells.each(function(c) {
21348             
21349             if(c.row < 5){
21350                 return;
21351             }
21352             
21353             var ev = c.events;
21354             
21355             var r = 4;
21356             if(c.row != c.events.length){
21357                 r = 4 - (4 - (c.row - c.events.length));
21358             }
21359             
21360             c.events = ev.slice(0, r);
21361             c.more = ev.slice(r);
21362             
21363             if(c.more.length && c.more.length == 1){
21364                 c.events.push(c.more.pop());
21365             }
21366             
21367             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21368             
21369         });
21370             
21371         this.cells.each(function(c) {
21372             
21373             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21374             
21375             
21376             for (var e = 0; e < c.events.length; e++){
21377                 var ev = c.events[e];
21378                 var rows = ev.rows;
21379                 
21380                 for(var i = 0; i < rows.length; i++) {
21381                 
21382                     // how many rows should it span..
21383
21384                     var  cfg = {
21385                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21386                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21387
21388                         unselectable : "on",
21389                         cn : [
21390                             {
21391                                 cls: 'fc-event-inner',
21392                                 cn : [
21393     //                                {
21394     //                                  tag:'span',
21395     //                                  cls: 'fc-event-time',
21396     //                                  html : cells.length > 1 ? '' : ev.time
21397     //                                },
21398                                     {
21399                                       tag:'span',
21400                                       cls: 'fc-event-title',
21401                                       html : String.format('{0}', ev.title)
21402                                     }
21403
21404
21405                                 ]
21406                             },
21407                             {
21408                                 cls: 'ui-resizable-handle ui-resizable-e',
21409                                 html : '&nbsp;&nbsp;&nbsp'
21410                             }
21411
21412                         ]
21413                     };
21414
21415                     if (i == 0) {
21416                         cfg.cls += ' fc-event-start';
21417                     }
21418                     if ((i+1) == rows.length) {
21419                         cfg.cls += ' fc-event-end';
21420                     }
21421
21422                     var ctr = _this.el.select('.fc-event-container',true).first();
21423                     var cg = ctr.createChild(cfg);
21424
21425                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21426                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21427
21428                     var r = (c.more.length) ? 1 : 0;
21429                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21430                     cg.setWidth(ebox.right - sbox.x -2);
21431
21432                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21433                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21434                     cg.on('click', _this.onEventClick, _this, ev);
21435
21436                     ev.els.push(cg);
21437                     
21438                 }
21439                 
21440             }
21441             
21442             
21443             if(c.more.length){
21444                 var  cfg = {
21445                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21446                     style : 'position: absolute',
21447                     unselectable : "on",
21448                     cn : [
21449                         {
21450                             cls: 'fc-event-inner',
21451                             cn : [
21452                                 {
21453                                   tag:'span',
21454                                   cls: 'fc-event-title',
21455                                   html : 'More'
21456                                 }
21457
21458
21459                             ]
21460                         },
21461                         {
21462                             cls: 'ui-resizable-handle ui-resizable-e',
21463                             html : '&nbsp;&nbsp;&nbsp'
21464                         }
21465
21466                     ]
21467                 };
21468
21469                 var ctr = _this.el.select('.fc-event-container',true).first();
21470                 var cg = ctr.createChild(cfg);
21471
21472                 var sbox = c.select('.fc-day-content',true).first().getBox();
21473                 var ebox = c.select('.fc-day-content',true).first().getBox();
21474                 //Roo.log(cg);
21475                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21476                 cg.setWidth(ebox.right - sbox.x -2);
21477
21478                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21479                 
21480             }
21481             
21482         });
21483         
21484         
21485         
21486     },
21487     
21488     onEventEnter: function (e, el,event,d) {
21489         this.fireEvent('evententer', this, el, event);
21490     },
21491     
21492     onEventLeave: function (e, el,event,d) {
21493         this.fireEvent('eventleave', this, el, event);
21494     },
21495     
21496     onEventClick: function (e, el,event,d) {
21497         this.fireEvent('eventclick', this, el, event);
21498     },
21499     
21500     onMonthChange: function () {
21501         this.store.load();
21502     },
21503     
21504     onMoreEventClick: function(e, el, more)
21505     {
21506         var _this = this;
21507         
21508         this.calpopover.placement = 'right';
21509         this.calpopover.setTitle('More');
21510         
21511         this.calpopover.setContent('');
21512         
21513         var ctr = this.calpopover.el.select('.popover-content', true).first();
21514         
21515         Roo.each(more, function(m){
21516             var cfg = {
21517                 cls : 'fc-event-hori fc-event-draggable',
21518                 html : m.title
21519             };
21520             var cg = ctr.createChild(cfg);
21521             
21522             cg.on('click', _this.onEventClick, _this, m);
21523         });
21524         
21525         this.calpopover.show(el);
21526         
21527         
21528     },
21529     
21530     onLoad: function () 
21531     {   
21532         this.calevents = [];
21533         var cal = this;
21534         
21535         if(this.store.getCount() > 0){
21536             this.store.data.each(function(d){
21537                cal.addItem({
21538                     id : d.data.id,
21539                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21540                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21541                     time : d.data.start_time,
21542                     title : d.data.title,
21543                     description : d.data.description,
21544                     venue : d.data.venue
21545                 });
21546             });
21547         }
21548         
21549         this.renderEvents();
21550         
21551         if(this.calevents.length && this.loadMask){
21552             this.maskEl.hide();
21553         }
21554     },
21555     
21556     onBeforeLoad: function()
21557     {
21558         this.clearEvents();
21559         if(this.loadMask){
21560             this.maskEl.show();
21561         }
21562     }
21563 });
21564
21565  
21566  /*
21567  * - LGPL
21568  *
21569  * element
21570  * 
21571  */
21572
21573 /**
21574  * @class Roo.bootstrap.Popover
21575  * @extends Roo.bootstrap.Component
21576  * @parent none builder
21577  * @children Roo.bootstrap.Component
21578  * Bootstrap Popover class
21579  * @cfg {String} html contents of the popover   (or false to use children..)
21580  * @cfg {String} title of popover (or false to hide)
21581  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21582  * @cfg {String} trigger click || hover (or false to trigger manually)
21583  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21584  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21585  *      - if false and it has a 'parent' then it will be automatically added to that element
21586  *      - if string - Roo.get  will be called 
21587  * @cfg {Number} delay - delay before showing
21588  
21589  * @constructor
21590  * Create a new Popover
21591  * @param {Object} config The config object
21592  */
21593
21594 Roo.bootstrap.Popover = function(config){
21595     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21596     
21597     this.addEvents({
21598         // raw events
21599          /**
21600          * @event show
21601          * After the popover show
21602          * 
21603          * @param {Roo.bootstrap.Popover} this
21604          */
21605         "show" : true,
21606         /**
21607          * @event hide
21608          * After the popover hide
21609          * 
21610          * @param {Roo.bootstrap.Popover} this
21611          */
21612         "hide" : true
21613     });
21614 };
21615
21616 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21617     
21618     title: false,
21619     html: false,
21620     
21621     placement : 'right',
21622     trigger : 'hover', // hover
21623     modal : false,
21624     delay : 0,
21625     
21626     over: false,
21627     
21628     can_build_overlaid : false,
21629     
21630     maskEl : false, // the mask element
21631     headerEl : false,
21632     contentEl : false,
21633     alignEl : false, // when show is called with an element - this get's stored.
21634     
21635     getChildContainer : function()
21636     {
21637         return this.contentEl;
21638         
21639     },
21640     getPopoverHeader : function()
21641     {
21642         this.title = true; // flag not to hide it..
21643         this.headerEl.addClass('p-0');
21644         return this.headerEl
21645     },
21646     
21647     
21648     getAutoCreate : function(){
21649          
21650         var cfg = {
21651            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21652            style: 'display:block',
21653            cn : [
21654                 {
21655                     cls : 'arrow'
21656                 },
21657                 {
21658                     cls : 'popover-inner ',
21659                     cn : [
21660                         {
21661                             tag: 'h3',
21662                             cls: 'popover-title popover-header',
21663                             html : this.title === false ? '' : this.title
21664                         },
21665                         {
21666                             cls : 'popover-content popover-body '  + (this.cls || ''),
21667                             html : this.html || ''
21668                         }
21669                     ]
21670                     
21671                 }
21672            ]
21673         };
21674         
21675         return cfg;
21676     },
21677     /**
21678      * @param {string} the title
21679      */
21680     setTitle: function(str)
21681     {
21682         this.title = str;
21683         if (this.el) {
21684             this.headerEl.dom.innerHTML = str;
21685         }
21686         
21687     },
21688     /**
21689      * @param {string} the body content
21690      */
21691     setContent: function(str)
21692     {
21693         this.html = str;
21694         if (this.contentEl) {
21695             this.contentEl.dom.innerHTML = str;
21696         }
21697         
21698     },
21699     // as it get's added to the bottom of the page.
21700     onRender : function(ct, position)
21701     {
21702         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21703         
21704         
21705         
21706         if(!this.el){
21707             var cfg = Roo.apply({},  this.getAutoCreate());
21708             cfg.id = Roo.id();
21709             
21710             if (this.cls) {
21711                 cfg.cls += ' ' + this.cls;
21712             }
21713             if (this.style) {
21714                 cfg.style = this.style;
21715             }
21716             //Roo.log("adding to ");
21717             this.el = Roo.get(document.body).createChild(cfg, position);
21718 //            Roo.log(this.el);
21719         }
21720         
21721         this.contentEl = this.el.select('.popover-content',true).first();
21722         this.headerEl =  this.el.select('.popover-title',true).first();
21723         
21724         var nitems = [];
21725         if(typeof(this.items) != 'undefined'){
21726             var items = this.items;
21727             delete this.items;
21728
21729             for(var i =0;i < items.length;i++) {
21730                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21731             }
21732         }
21733
21734         this.items = nitems;
21735         
21736         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21737         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21738         
21739         
21740         
21741         this.initEvents();
21742     },
21743     
21744     resizeMask : function()
21745     {
21746         this.maskEl.setSize(
21747             Roo.lib.Dom.getViewWidth(true),
21748             Roo.lib.Dom.getViewHeight(true)
21749         );
21750     },
21751     
21752     initEvents : function()
21753     {
21754         
21755         if (!this.modal) { 
21756             Roo.bootstrap.Popover.register(this);
21757         }
21758          
21759         this.arrowEl = this.el.select('.arrow',true).first();
21760         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21761         this.el.enableDisplayMode('block');
21762         this.el.hide();
21763  
21764         
21765         if (this.over === false && !this.parent()) {
21766             return; 
21767         }
21768         if (this.triggers === false) {
21769             return;
21770         }
21771          
21772         // support parent
21773         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21774         var triggers = this.trigger ? this.trigger.split(' ') : [];
21775         Roo.each(triggers, function(trigger) {
21776         
21777             if (trigger == 'click') {
21778                 on_el.on('click', this.toggle, this);
21779             } else if (trigger != 'manual') {
21780                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21781                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21782       
21783                 on_el.on(eventIn  ,this.enter, this);
21784                 on_el.on(eventOut, this.leave, this);
21785             }
21786         }, this);
21787     },
21788     
21789     
21790     // private
21791     timeout : null,
21792     hoverState : null,
21793     
21794     toggle : function () {
21795         this.hoverState == 'in' ? this.leave() : this.enter();
21796     },
21797     
21798     enter : function () {
21799         
21800         clearTimeout(this.timeout);
21801     
21802         this.hoverState = 'in';
21803     
21804         if (!this.delay || !this.delay.show) {
21805             this.show();
21806             return;
21807         }
21808         var _t = this;
21809         this.timeout = setTimeout(function () {
21810             if (_t.hoverState == 'in') {
21811                 _t.show();
21812             }
21813         }, this.delay.show)
21814     },
21815     
21816     leave : function() {
21817         clearTimeout(this.timeout);
21818     
21819         this.hoverState = 'out';
21820     
21821         if (!this.delay || !this.delay.hide) {
21822             this.hide();
21823             return;
21824         }
21825         var _t = this;
21826         this.timeout = setTimeout(function () {
21827             if (_t.hoverState == 'out') {
21828                 _t.hide();
21829             }
21830         }, this.delay.hide)
21831     },
21832     
21833     /**
21834      * update the position of the dialog
21835      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21836      * 
21837      *
21838      */
21839     
21840     doAlign : function()
21841     {
21842         
21843         if (this.alignEl) {
21844             this.updatePosition(this.placement, true);
21845              
21846         } else {
21847             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21848             var es = this.el.getSize();
21849             var x = Roo.lib.Dom.getViewWidth()/2;
21850             var y = Roo.lib.Dom.getViewHeight()/2;
21851             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21852             
21853         }
21854
21855          
21856          
21857         
21858         
21859     },
21860     
21861     /**
21862      * Show the popover
21863      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21864      * @param {string} (left|right|top|bottom) position
21865      */
21866     show : function (on_el, placement)
21867     {
21868         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21869         on_el = on_el || false; // default to false
21870          
21871         if (!on_el) {
21872             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21873                 on_el = this.parent().el;
21874             } else if (this.over) {
21875                 on_el = Roo.get(this.over);
21876             }
21877             
21878         }
21879         
21880         this.alignEl = Roo.get( on_el );
21881
21882         if (!this.el) {
21883             this.render(document.body);
21884         }
21885         
21886         
21887          
21888         
21889         if (this.title === false) {
21890             this.headerEl.hide();
21891         }
21892         
21893        
21894         this.el.show();
21895         this.el.dom.style.display = 'block';
21896          
21897         this.doAlign();
21898         
21899         //var arrow = this.el.select('.arrow',true).first();
21900         //arrow.set(align[2], 
21901         
21902         this.el.addClass('in');
21903         
21904          
21905         
21906         this.hoverState = 'in';
21907         
21908         if (this.modal) {
21909             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21910             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21911             this.maskEl.dom.style.display = 'block';
21912             this.maskEl.addClass('show');
21913         }
21914         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21915  
21916         this.fireEvent('show', this);
21917         
21918     },
21919     /**
21920      * fire this manually after loading a grid in the table for example
21921      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21922      * @param {Boolean} try and move it if we cant get right position.
21923      */
21924     updatePosition : function(placement, try_move)
21925     {
21926         // allow for calling with no parameters
21927         placement = placement   ? placement :  this.placement;
21928         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21929         
21930         this.el.removeClass([
21931             'fade','top','bottom', 'left', 'right','in',
21932             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21933         ]);
21934         this.el.addClass(placement + ' bs-popover-' + placement);
21935         
21936         if (!this.alignEl ) {
21937             return false;
21938         }
21939         
21940         switch (placement) {
21941             case 'right':
21942                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21943                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21944                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21945                     //normal display... or moved up/down.
21946                     this.el.setXY(offset);
21947                     var xy = this.alignEl.getAnchorXY('tr', false);
21948                     xy[0]+=2;xy[1]+=5;
21949                     this.arrowEl.setXY(xy);
21950                     return true;
21951                 }
21952                 // continue through...
21953                 return this.updatePosition('left', false);
21954                 
21955             
21956             case 'left':
21957                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21958                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21959                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21960                     //normal display... or moved up/down.
21961                     this.el.setXY(offset);
21962                     var xy = this.alignEl.getAnchorXY('tl', false);
21963                     xy[0]-=10;xy[1]+=5; // << fix me
21964                     this.arrowEl.setXY(xy);
21965                     return true;
21966                 }
21967                 // call self...
21968                 return this.updatePosition('right', false);
21969             
21970             case 'top':
21971                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21972                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21973                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21974                     //normal display... or moved up/down.
21975                     this.el.setXY(offset);
21976                     var xy = this.alignEl.getAnchorXY('t', false);
21977                     xy[1]-=10; // << fix me
21978                     this.arrowEl.setXY(xy);
21979                     return true;
21980                 }
21981                 // fall through
21982                return this.updatePosition('bottom', false);
21983             
21984             case 'bottom':
21985                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21986                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21987                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21988                     //normal display... or moved up/down.
21989                     this.el.setXY(offset);
21990                     var xy = this.alignEl.getAnchorXY('b', false);
21991                      xy[1]+=2; // << fix me
21992                     this.arrowEl.setXY(xy);
21993                     return true;
21994                 }
21995                 // fall through
21996                 return this.updatePosition('top', false);
21997                 
21998             
21999         }
22000         
22001         
22002         return false;
22003     },
22004     
22005     hide : function()
22006     {
22007         this.el.setXY([0,0]);
22008         this.el.removeClass('in');
22009         this.el.hide();
22010         this.hoverState = null;
22011         this.maskEl.hide(); // always..
22012         this.fireEvent('hide', this);
22013     }
22014     
22015 });
22016
22017
22018 Roo.apply(Roo.bootstrap.Popover, {
22019
22020     alignment : {
22021         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
22022         'right' : ['l-br', [10,0], 'right bs-popover-right'],
22023         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
22024         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
22025     },
22026     
22027     zIndex : 20001,
22028
22029     clickHander : false,
22030     
22031     
22032
22033     onMouseDown : function(e)
22034     {
22035         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
22036             /// what is nothing is showing..
22037             this.hideAll();
22038         }
22039          
22040     },
22041     
22042     
22043     popups : [],
22044     
22045     register : function(popup)
22046     {
22047         if (!Roo.bootstrap.Popover.clickHandler) {
22048             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22049         }
22050         // hide other popups.
22051         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22052         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22053         this.hideAll(); //<< why?
22054         //this.popups.push(popup);
22055     },
22056     hideAll : function()
22057     {
22058         this.popups.forEach(function(p) {
22059             p.hide();
22060         });
22061     },
22062     onShow : function() {
22063         Roo.bootstrap.Popover.popups.push(this);
22064     },
22065     onHide : function() {
22066         Roo.bootstrap.Popover.popups.remove(this);
22067     } 
22068
22069 });
22070 /**
22071  * @class Roo.bootstrap.PopoverNav
22072  * @extends Roo.bootstrap.nav.Simplebar
22073  * @parent Roo.bootstrap.Popover
22074  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22075  * @licence LGPL
22076  * Bootstrap Popover header navigation class
22077  * FIXME? should this go under nav?
22078  *
22079  * 
22080  * @constructor
22081  * Create a new Popover Header Navigation 
22082  * @param {Object} config The config object
22083  */
22084
22085 Roo.bootstrap.PopoverNav = function(config){
22086     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22087 };
22088
22089 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22090     
22091     
22092     container_method : 'getPopoverHeader' 
22093     
22094      
22095     
22096     
22097    
22098 });
22099
22100  
22101
22102  /*
22103  * - LGPL
22104  *
22105  * Progress
22106  * 
22107  */
22108
22109 /**
22110  * @class Roo.bootstrap.Progress
22111  * @extends Roo.bootstrap.Component
22112  * @children Roo.bootstrap.ProgressBar
22113  * Bootstrap Progress class
22114  * @cfg {Boolean} striped striped of the progress bar
22115  * @cfg {Boolean} active animated of the progress bar
22116  * 
22117  * 
22118  * @constructor
22119  * Create a new Progress
22120  * @param {Object} config The config object
22121  */
22122
22123 Roo.bootstrap.Progress = function(config){
22124     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22125 };
22126
22127 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22128     
22129     striped : false,
22130     active: false,
22131     
22132     getAutoCreate : function(){
22133         var cfg = {
22134             tag: 'div',
22135             cls: 'progress'
22136         };
22137         
22138         
22139         if(this.striped){
22140             cfg.cls += ' progress-striped';
22141         }
22142       
22143         if(this.active){
22144             cfg.cls += ' active';
22145         }
22146         
22147         
22148         return cfg;
22149     }
22150    
22151 });
22152
22153  
22154
22155  /*
22156  * - LGPL
22157  *
22158  * ProgressBar
22159  * 
22160  */
22161
22162 /**
22163  * @class Roo.bootstrap.ProgressBar
22164  * @extends Roo.bootstrap.Component
22165  * Bootstrap ProgressBar class
22166  * @cfg {Number} aria_valuenow aria-value now
22167  * @cfg {Number} aria_valuemin aria-value min
22168  * @cfg {Number} aria_valuemax aria-value max
22169  * @cfg {String} label label for the progress bar
22170  * @cfg {String} panel (success | info | warning | danger )
22171  * @cfg {String} role role of the progress bar
22172  * @cfg {String} sr_only text
22173  * 
22174  * 
22175  * @constructor
22176  * Create a new ProgressBar
22177  * @param {Object} config The config object
22178  */
22179
22180 Roo.bootstrap.ProgressBar = function(config){
22181     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22182 };
22183
22184 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22185     
22186     aria_valuenow : 0,
22187     aria_valuemin : 0,
22188     aria_valuemax : 100,
22189     label : false,
22190     panel : false,
22191     role : false,
22192     sr_only: false,
22193     
22194     getAutoCreate : function()
22195     {
22196         
22197         var cfg = {
22198             tag: 'div',
22199             cls: 'progress-bar',
22200             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22201         };
22202         
22203         if(this.sr_only){
22204             cfg.cn = {
22205                 tag: 'span',
22206                 cls: 'sr-only',
22207                 html: this.sr_only
22208             }
22209         }
22210         
22211         if(this.role){
22212             cfg.role = this.role;
22213         }
22214         
22215         if(this.aria_valuenow){
22216             cfg['aria-valuenow'] = this.aria_valuenow;
22217         }
22218         
22219         if(this.aria_valuemin){
22220             cfg['aria-valuemin'] = this.aria_valuemin;
22221         }
22222         
22223         if(this.aria_valuemax){
22224             cfg['aria-valuemax'] = this.aria_valuemax;
22225         }
22226         
22227         if(this.label && !this.sr_only){
22228             cfg.html = this.label;
22229         }
22230         
22231         if(this.panel){
22232             cfg.cls += ' progress-bar-' + this.panel;
22233         }
22234         
22235         return cfg;
22236     },
22237     
22238     update : function(aria_valuenow)
22239     {
22240         this.aria_valuenow = aria_valuenow;
22241         
22242         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22243     }
22244    
22245 });
22246
22247  
22248
22249  /**
22250  * @class Roo.bootstrap.TabGroup
22251  * @extends Roo.bootstrap.Column
22252  * @children Roo.bootstrap.TabPanel
22253  * Bootstrap Column class
22254  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22255  * @cfg {Boolean} carousel true to make the group behave like a carousel
22256  * @cfg {Boolean} bullets show bullets for the panels
22257  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22258  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22259  * @cfg {Boolean} showarrow (true|false) show arrow default true
22260  * 
22261  * @constructor
22262  * Create a new TabGroup
22263  * @param {Object} config The config object
22264  */
22265
22266 Roo.bootstrap.TabGroup = function(config){
22267     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22268     if (!this.navId) {
22269         this.navId = Roo.id();
22270     }
22271     this.tabs = [];
22272     Roo.bootstrap.TabGroup.register(this);
22273     
22274 };
22275
22276 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22277     
22278     carousel : false,
22279     transition : false,
22280     bullets : 0,
22281     timer : 0,
22282     autoslide : false,
22283     slideFn : false,
22284     slideOnTouch : false,
22285     showarrow : true,
22286     
22287     getAutoCreate : function()
22288     {
22289         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22290         
22291         cfg.cls += ' tab-content';
22292         
22293         if (this.carousel) {
22294             cfg.cls += ' carousel slide';
22295             
22296             cfg.cn = [{
22297                cls : 'carousel-inner',
22298                cn : []
22299             }];
22300         
22301             if(this.bullets  && !Roo.isTouch){
22302                 
22303                 var bullets = {
22304                     cls : 'carousel-bullets',
22305                     cn : []
22306                 };
22307                
22308                 if(this.bullets_cls){
22309                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22310                 }
22311                 
22312                 bullets.cn.push({
22313                     cls : 'clear'
22314                 });
22315                 
22316                 cfg.cn[0].cn.push(bullets);
22317             }
22318             
22319             if(this.showarrow){
22320                 cfg.cn[0].cn.push({
22321                     tag : 'div',
22322                     class : 'carousel-arrow',
22323                     cn : [
22324                         {
22325                             tag : 'div',
22326                             class : 'carousel-prev',
22327                             cn : [
22328                                 {
22329                                     tag : 'i',
22330                                     class : 'fa fa-chevron-left'
22331                                 }
22332                             ]
22333                         },
22334                         {
22335                             tag : 'div',
22336                             class : 'carousel-next',
22337                             cn : [
22338                                 {
22339                                     tag : 'i',
22340                                     class : 'fa fa-chevron-right'
22341                                 }
22342                             ]
22343                         }
22344                     ]
22345                 });
22346             }
22347             
22348         }
22349         
22350         return cfg;
22351     },
22352     
22353     initEvents:  function()
22354     {
22355 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22356 //            this.el.on("touchstart", this.onTouchStart, this);
22357 //        }
22358         
22359         if(this.autoslide){
22360             var _this = this;
22361             
22362             this.slideFn = window.setInterval(function() {
22363                 _this.showPanelNext();
22364             }, this.timer);
22365         }
22366         
22367         if(this.showarrow){
22368             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22369             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22370         }
22371         
22372         
22373     },
22374     
22375 //    onTouchStart : function(e, el, o)
22376 //    {
22377 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22378 //            return;
22379 //        }
22380 //        
22381 //        this.showPanelNext();
22382 //    },
22383     
22384     
22385     getChildContainer : function()
22386     {
22387         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22388     },
22389     
22390     /**
22391     * register a Navigation item
22392     * @param {Roo.bootstrap.nav.Item} the navitem to add
22393     */
22394     register : function(item)
22395     {
22396         this.tabs.push( item);
22397         item.navId = this.navId; // not really needed..
22398         this.addBullet();
22399     
22400     },
22401     
22402     getActivePanel : function()
22403     {
22404         var r = false;
22405         Roo.each(this.tabs, function(t) {
22406             if (t.active) {
22407                 r = t;
22408                 return false;
22409             }
22410             return null;
22411         });
22412         return r;
22413         
22414     },
22415     getPanelByName : function(n)
22416     {
22417         var r = false;
22418         Roo.each(this.tabs, function(t) {
22419             if (t.tabId == n) {
22420                 r = t;
22421                 return false;
22422             }
22423             return null;
22424         });
22425         return r;
22426     },
22427     indexOfPanel : function(p)
22428     {
22429         var r = false;
22430         Roo.each(this.tabs, function(t,i) {
22431             if (t.tabId == p.tabId) {
22432                 r = i;
22433                 return false;
22434             }
22435             return null;
22436         });
22437         return r;
22438     },
22439     /**
22440      * show a specific panel
22441      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22442      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22443      */
22444     showPanel : function (pan)
22445     {
22446         if(this.transition || typeof(pan) == 'undefined'){
22447             Roo.log("waiting for the transitionend");
22448             return false;
22449         }
22450         
22451         if (typeof(pan) == 'number') {
22452             pan = this.tabs[pan];
22453         }
22454         
22455         if (typeof(pan) == 'string') {
22456             pan = this.getPanelByName(pan);
22457         }
22458         
22459         var cur = this.getActivePanel();
22460         
22461         if(!pan || !cur){
22462             Roo.log('pan or acitve pan is undefined');
22463             return false;
22464         }
22465         
22466         if (pan.tabId == this.getActivePanel().tabId) {
22467             return true;
22468         }
22469         
22470         if (false === cur.fireEvent('beforedeactivate')) {
22471             return false;
22472         }
22473         
22474         if(this.bullets > 0 && !Roo.isTouch){
22475             this.setActiveBullet(this.indexOfPanel(pan));
22476         }
22477         
22478         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22479             
22480             //class="carousel-item carousel-item-next carousel-item-left"
22481             
22482             this.transition = true;
22483             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22484             var lr = dir == 'next' ? 'left' : 'right';
22485             pan.el.addClass(dir); // or prev
22486             pan.el.addClass('carousel-item-' + dir); // or prev
22487             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22488             cur.el.addClass(lr); // or right
22489             pan.el.addClass(lr);
22490             cur.el.addClass('carousel-item-' +lr); // or right
22491             pan.el.addClass('carousel-item-' +lr);
22492             
22493             
22494             var _this = this;
22495             cur.el.on('transitionend', function() {
22496                 Roo.log("trans end?");
22497                 
22498                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22499                 pan.setActive(true);
22500                 
22501                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22502                 cur.setActive(false);
22503                 
22504                 _this.transition = false;
22505                 
22506             }, this, { single:  true } );
22507             
22508             return true;
22509         }
22510         
22511         cur.setActive(false);
22512         pan.setActive(true);
22513         
22514         return true;
22515         
22516     },
22517     showPanelNext : function()
22518     {
22519         var i = this.indexOfPanel(this.getActivePanel());
22520         
22521         if (i >= this.tabs.length - 1 && !this.autoslide) {
22522             return;
22523         }
22524         
22525         if (i >= this.tabs.length - 1 && this.autoslide) {
22526             i = -1;
22527         }
22528         
22529         this.showPanel(this.tabs[i+1]);
22530     },
22531     
22532     showPanelPrev : function()
22533     {
22534         var i = this.indexOfPanel(this.getActivePanel());
22535         
22536         if (i  < 1 && !this.autoslide) {
22537             return;
22538         }
22539         
22540         if (i < 1 && this.autoslide) {
22541             i = this.tabs.length;
22542         }
22543         
22544         this.showPanel(this.tabs[i-1]);
22545     },
22546     
22547     
22548     addBullet: function()
22549     {
22550         if(!this.bullets || Roo.isTouch){
22551             return;
22552         }
22553         var ctr = this.el.select('.carousel-bullets',true).first();
22554         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22555         var bullet = ctr.createChild({
22556             cls : 'bullet bullet-' + i
22557         },ctr.dom.lastChild);
22558         
22559         
22560         var _this = this;
22561         
22562         bullet.on('click', (function(e, el, o, ii, t){
22563
22564             e.preventDefault();
22565
22566             this.showPanel(ii);
22567
22568             if(this.autoslide && this.slideFn){
22569                 clearInterval(this.slideFn);
22570                 this.slideFn = window.setInterval(function() {
22571                     _this.showPanelNext();
22572                 }, this.timer);
22573             }
22574
22575         }).createDelegate(this, [i, bullet], true));
22576                 
22577         
22578     },
22579      
22580     setActiveBullet : function(i)
22581     {
22582         if(Roo.isTouch){
22583             return;
22584         }
22585         
22586         Roo.each(this.el.select('.bullet', true).elements, function(el){
22587             el.removeClass('selected');
22588         });
22589
22590         var bullet = this.el.select('.bullet-' + i, true).first();
22591         
22592         if(!bullet){
22593             return;
22594         }
22595         
22596         bullet.addClass('selected');
22597     }
22598     
22599     
22600   
22601 });
22602
22603  
22604
22605  
22606  
22607 Roo.apply(Roo.bootstrap.TabGroup, {
22608     
22609     groups: {},
22610      /**
22611     * register a Navigation Group
22612     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22613     */
22614     register : function(navgrp)
22615     {
22616         this.groups[navgrp.navId] = navgrp;
22617         
22618     },
22619     /**
22620     * fetch a Navigation Group based on the navigation ID
22621     * if one does not exist , it will get created.
22622     * @param {string} the navgroup to add
22623     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22624     */
22625     get: function(navId) {
22626         if (typeof(this.groups[navId]) == 'undefined') {
22627             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22628         }
22629         return this.groups[navId] ;
22630     }
22631     
22632     
22633     
22634 });
22635
22636  /*
22637  * - LGPL
22638  *
22639  * TabPanel
22640  * 
22641  */
22642
22643 /**
22644  * @class Roo.bootstrap.TabPanel
22645  * @extends Roo.bootstrap.Component
22646  * @children Roo.bootstrap.Component
22647  * Bootstrap TabPanel class
22648  * @cfg {Boolean} active panel active
22649  * @cfg {String} html panel content
22650  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22651  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22652  * @cfg {String} href click to link..
22653  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22654  * 
22655  * 
22656  * @constructor
22657  * Create a new TabPanel
22658  * @param {Object} config The config object
22659  */
22660
22661 Roo.bootstrap.TabPanel = function(config){
22662     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22663     this.addEvents({
22664         /**
22665              * @event changed
22666              * Fires when the active status changes
22667              * @param {Roo.bootstrap.TabPanel} this
22668              * @param {Boolean} state the new state
22669             
22670          */
22671         'changed': true,
22672         /**
22673              * @event beforedeactivate
22674              * Fires before a tab is de-activated - can be used to do validation on a form.
22675              * @param {Roo.bootstrap.TabPanel} this
22676              * @return {Boolean} false if there is an error
22677             
22678          */
22679         'beforedeactivate': true
22680      });
22681     
22682     this.tabId = this.tabId || Roo.id();
22683   
22684 };
22685
22686 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22687     
22688     active: false,
22689     html: false,
22690     tabId: false,
22691     navId : false,
22692     href : '',
22693     touchSlide : false,
22694     getAutoCreate : function(){
22695         
22696         
22697         var cfg = {
22698             tag: 'div',
22699             // item is needed for carousel - not sure if it has any effect otherwise
22700             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22701             html: this.html || ''
22702         };
22703         
22704         if(this.active){
22705             cfg.cls += ' active';
22706         }
22707         
22708         if(this.tabId){
22709             cfg.tabId = this.tabId;
22710         }
22711         
22712         
22713         
22714         return cfg;
22715     },
22716     
22717     initEvents:  function()
22718     {
22719         var p = this.parent();
22720         
22721         this.navId = this.navId || p.navId;
22722         
22723         if (typeof(this.navId) != 'undefined') {
22724             // not really needed.. but just in case.. parent should be a NavGroup.
22725             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22726             
22727             tg.register(this);
22728             
22729             var i = tg.tabs.length - 1;
22730             
22731             if(this.active && tg.bullets > 0 && i < tg.bullets){
22732                 tg.setActiveBullet(i);
22733             }
22734         }
22735         
22736         this.el.on('click', this.onClick, this);
22737         
22738         if(Roo.isTouch && this.touchSlide){
22739             this.el.on("touchstart", this.onTouchStart, this);
22740             this.el.on("touchmove", this.onTouchMove, this);
22741             this.el.on("touchend", this.onTouchEnd, this);
22742         }
22743         
22744     },
22745     
22746     onRender : function(ct, position)
22747     {
22748         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22749     },
22750     
22751     setActive : function(state)
22752     {
22753         Roo.log("panel - set active " + this.tabId + "=" + state);
22754         
22755         this.active = state;
22756         if (!state) {
22757             this.el.removeClass('active');
22758             
22759         } else  if (!this.el.hasClass('active')) {
22760             this.el.addClass('active');
22761         }
22762         
22763         this.fireEvent('changed', this, state);
22764     },
22765     
22766     onClick : function(e)
22767     {
22768         e.preventDefault();
22769         
22770         if(!this.href.length){
22771             return;
22772         }
22773         
22774         window.location.href = this.href;
22775     },
22776     
22777     startX : 0,
22778     startY : 0,
22779     endX : 0,
22780     endY : 0,
22781     swiping : false,
22782     
22783     onTouchStart : function(e)
22784     {
22785         this.swiping = false;
22786         
22787         this.startX = e.browserEvent.touches[0].clientX;
22788         this.startY = e.browserEvent.touches[0].clientY;
22789     },
22790     
22791     onTouchMove : function(e)
22792     {
22793         this.swiping = true;
22794         
22795         this.endX = e.browserEvent.touches[0].clientX;
22796         this.endY = e.browserEvent.touches[0].clientY;
22797     },
22798     
22799     onTouchEnd : function(e)
22800     {
22801         if(!this.swiping){
22802             this.onClick(e);
22803             return;
22804         }
22805         
22806         var tabGroup = this.parent();
22807         
22808         if(this.endX > this.startX){ // swiping right
22809             tabGroup.showPanelPrev();
22810             return;
22811         }
22812         
22813         if(this.startX > this.endX){ // swiping left
22814             tabGroup.showPanelNext();
22815             return;
22816         }
22817     }
22818     
22819     
22820 });
22821  
22822
22823  
22824
22825  /*
22826  * - LGPL
22827  *
22828  * DateField
22829  * 
22830  */
22831
22832 /**
22833  * @class Roo.bootstrap.form.DateField
22834  * @extends Roo.bootstrap.form.Input
22835  * Bootstrap DateField class
22836  * @cfg {Number} weekStart default 0
22837  * @cfg {String} viewMode default empty, (months|years)
22838  * @cfg {String} minViewMode default empty, (months|years)
22839  * @cfg {Number} startDate default -Infinity
22840  * @cfg {Number} endDate default Infinity
22841  * @cfg {Boolean} todayHighlight default false
22842  * @cfg {Boolean} todayBtn default false
22843  * @cfg {Boolean} calendarWeeks default false
22844  * @cfg {Object} daysOfWeekDisabled default empty
22845  * @cfg {Boolean} singleMode default false (true | false)
22846  * 
22847  * @cfg {Boolean} keyboardNavigation default true
22848  * @cfg {String} language default en
22849  * 
22850  * @constructor
22851  * Create a new DateField
22852  * @param {Object} config The config object
22853  */
22854
22855 Roo.bootstrap.form.DateField = function(config){
22856     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22857      this.addEvents({
22858             /**
22859              * @event show
22860              * Fires when this field show.
22861              * @param {Roo.bootstrap.form.DateField} this
22862              * @param {Mixed} date The date value
22863              */
22864             show : true,
22865             /**
22866              * @event show
22867              * Fires when this field hide.
22868              * @param {Roo.bootstrap.form.DateField} this
22869              * @param {Mixed} date The date value
22870              */
22871             hide : true,
22872             /**
22873              * @event select
22874              * Fires when select a date.
22875              * @param {Roo.bootstrap.form.DateField} this
22876              * @param {Mixed} date The date value
22877              */
22878             select : true,
22879             /**
22880              * @event beforeselect
22881              * Fires when before select a date.
22882              * @param {Roo.bootstrap.form.DateField} this
22883              * @param {Mixed} date The date value
22884              */
22885             beforeselect : true
22886         });
22887 };
22888
22889 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22890     
22891     /**
22892      * @cfg {String} format
22893      * The default date format string which can be overriden for localization support.  The format must be
22894      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22895      */
22896     format : "m/d/y",
22897     /**
22898      * @cfg {String} altFormats
22899      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22900      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22901      */
22902     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22903     
22904     weekStart : 0,
22905     
22906     viewMode : '',
22907     
22908     minViewMode : '',
22909     
22910     todayHighlight : false,
22911     
22912     todayBtn: false,
22913     
22914     language: 'en',
22915     
22916     keyboardNavigation: true,
22917     
22918     calendarWeeks: false,
22919     
22920     startDate: -Infinity,
22921     
22922     endDate: Infinity,
22923     
22924     daysOfWeekDisabled: [],
22925     
22926     _events: [],
22927     
22928     singleMode : false,
22929     
22930     UTCDate: function()
22931     {
22932         return new Date(Date.UTC.apply(Date, arguments));
22933     },
22934     
22935     UTCToday: function()
22936     {
22937         var today = new Date();
22938         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22939     },
22940     
22941     getDate: function() {
22942             var d = this.getUTCDate();
22943             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22944     },
22945     
22946     getUTCDate: function() {
22947             return this.date;
22948     },
22949     
22950     setDate: function(d) {
22951             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22952     },
22953     
22954     setUTCDate: function(d) {
22955             this.date = d;
22956             this.setValue(this.formatDate(this.date));
22957     },
22958         
22959     onRender: function(ct, position)
22960     {
22961         
22962         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22963         
22964         this.language = this.language || 'en';
22965         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22966         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22967         
22968         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22969         this.format = this.format || 'm/d/y';
22970         this.isInline = false;
22971         this.isInput = true;
22972         this.component = this.el.select('.add-on', true).first() || false;
22973         this.component = (this.component && this.component.length === 0) ? false : this.component;
22974         this.hasInput = this.component && this.inputEl().length;
22975         
22976         if (typeof(this.minViewMode === 'string')) {
22977             switch (this.minViewMode) {
22978                 case 'months':
22979                     this.minViewMode = 1;
22980                     break;
22981                 case 'years':
22982                     this.minViewMode = 2;
22983                     break;
22984                 default:
22985                     this.minViewMode = 0;
22986                     break;
22987             }
22988         }
22989         
22990         if (typeof(this.viewMode === 'string')) {
22991             switch (this.viewMode) {
22992                 case 'months':
22993                     this.viewMode = 1;
22994                     break;
22995                 case 'years':
22996                     this.viewMode = 2;
22997                     break;
22998                 default:
22999                     this.viewMode = 0;
23000                     break;
23001             }
23002         }
23003                 
23004         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
23005         
23006 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
23007         
23008         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23009         
23010         this.picker().on('mousedown', this.onMousedown, this);
23011         this.picker().on('click', this.onClick, this);
23012         
23013         this.picker().addClass('datepicker-dropdown');
23014         
23015         this.startViewMode = this.viewMode;
23016         
23017         if(this.singleMode){
23018             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
23019                 v.setVisibilityMode(Roo.Element.DISPLAY);
23020                 v.hide();
23021             });
23022             
23023             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23024                 v.setStyle('width', '189px');
23025             });
23026         }
23027         
23028         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
23029             if(!this.calendarWeeks){
23030                 v.remove();
23031                 return;
23032             }
23033             
23034             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23035             v.attr('colspan', function(i, val){
23036                 return parseInt(val) + 1;
23037             });
23038         });
23039                         
23040         
23041         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23042         
23043         this.setStartDate(this.startDate);
23044         this.setEndDate(this.endDate);
23045         
23046         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23047         
23048         this.fillDow();
23049         this.fillMonths();
23050         this.update();
23051         this.showMode();
23052         
23053         if(this.isInline) {
23054             this.showPopup();
23055         }
23056     },
23057     
23058     picker : function()
23059     {
23060         return this.pickerEl;
23061 //        return this.el.select('.datepicker', true).first();
23062     },
23063     
23064     fillDow: function()
23065     {
23066         var dowCnt = this.weekStart;
23067         
23068         var dow = {
23069             tag: 'tr',
23070             cn: [
23071                 
23072             ]
23073         };
23074         
23075         if(this.calendarWeeks){
23076             dow.cn.push({
23077                 tag: 'th',
23078                 cls: 'cw',
23079                 html: '&nbsp;'
23080             })
23081         }
23082         
23083         while (dowCnt < this.weekStart + 7) {
23084             dow.cn.push({
23085                 tag: 'th',
23086                 cls: 'dow',
23087                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23088             });
23089         }
23090         
23091         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23092     },
23093     
23094     fillMonths: function()
23095     {    
23096         var i = 0;
23097         var months = this.picker().select('>.datepicker-months td', true).first();
23098         
23099         months.dom.innerHTML = '';
23100         
23101         while (i < 12) {
23102             var month = {
23103                 tag: 'span',
23104                 cls: 'month',
23105                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23106             };
23107             
23108             months.createChild(month);
23109         }
23110         
23111     },
23112     
23113     update: function()
23114     {
23115         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;
23116         
23117         if (this.date < this.startDate) {
23118             this.viewDate = new Date(this.startDate);
23119         } else if (this.date > this.endDate) {
23120             this.viewDate = new Date(this.endDate);
23121         } else {
23122             this.viewDate = new Date(this.date);
23123         }
23124         
23125         this.fill();
23126     },
23127     
23128     fill: function() 
23129     {
23130         var d = new Date(this.viewDate),
23131                 year = d.getUTCFullYear(),
23132                 month = d.getUTCMonth(),
23133                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23134                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23135                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23136                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23137                 currentDate = this.date && this.date.valueOf(),
23138                 today = this.UTCToday();
23139         
23140         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23141         
23142 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23143         
23144 //        this.picker.select('>tfoot th.today').
23145 //                                              .text(dates[this.language].today)
23146 //                                              .toggle(this.todayBtn !== false);
23147     
23148         this.updateNavArrows();
23149         this.fillMonths();
23150                                                 
23151         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23152         
23153         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23154          
23155         prevMonth.setUTCDate(day);
23156         
23157         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23158         
23159         var nextMonth = new Date(prevMonth);
23160         
23161         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23162         
23163         nextMonth = nextMonth.valueOf();
23164         
23165         var fillMonths = false;
23166         
23167         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23168         
23169         while(prevMonth.valueOf() <= nextMonth) {
23170             var clsName = '';
23171             
23172             if (prevMonth.getUTCDay() === this.weekStart) {
23173                 if(fillMonths){
23174                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23175                 }
23176                     
23177                 fillMonths = {
23178                     tag: 'tr',
23179                     cn: []
23180                 };
23181                 
23182                 if(this.calendarWeeks){
23183                     // ISO 8601: First week contains first thursday.
23184                     // ISO also states week starts on Monday, but we can be more abstract here.
23185                     var
23186                     // Start of current week: based on weekstart/current date
23187                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23188                     // Thursday of this week
23189                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23190                     // First Thursday of year, year from thursday
23191                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23192                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23193                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23194                     
23195                     fillMonths.cn.push({
23196                         tag: 'td',
23197                         cls: 'cw',
23198                         html: calWeek
23199                     });
23200                 }
23201             }
23202             
23203             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23204                 clsName += ' old';
23205             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23206                 clsName += ' new';
23207             }
23208             if (this.todayHighlight &&
23209                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23210                 prevMonth.getUTCMonth() == today.getMonth() &&
23211                 prevMonth.getUTCDate() == today.getDate()) {
23212                 clsName += ' today';
23213             }
23214             
23215             if (currentDate && prevMonth.valueOf() === currentDate) {
23216                 clsName += ' active';
23217             }
23218             
23219             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23220                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23221                     clsName += ' disabled';
23222             }
23223             
23224             fillMonths.cn.push({
23225                 tag: 'td',
23226                 cls: 'day ' + clsName,
23227                 html: prevMonth.getDate()
23228             });
23229             
23230             prevMonth.setDate(prevMonth.getDate()+1);
23231         }
23232           
23233         var currentYear = this.date && this.date.getUTCFullYear();
23234         var currentMonth = this.date && this.date.getUTCMonth();
23235         
23236         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23237         
23238         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23239             v.removeClass('active');
23240             
23241             if(currentYear === year && k === currentMonth){
23242                 v.addClass('active');
23243             }
23244             
23245             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23246                 v.addClass('disabled');
23247             }
23248             
23249         });
23250         
23251         
23252         year = parseInt(year/10, 10) * 10;
23253         
23254         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23255         
23256         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23257         
23258         year -= 1;
23259         for (var i = -1; i < 11; i++) {
23260             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23261                 tag: 'span',
23262                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23263                 html: year
23264             });
23265             
23266             year += 1;
23267         }
23268     },
23269     
23270     showMode: function(dir) 
23271     {
23272         if (dir) {
23273             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23274         }
23275         
23276         Roo.each(this.picker().select('>div',true).elements, function(v){
23277             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23278             v.hide();
23279         });
23280         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23281     },
23282     
23283     place: function()
23284     {
23285         if(this.isInline) {
23286             return;
23287         }
23288         
23289         this.picker().removeClass(['bottom', 'top']);
23290         
23291         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23292             /*
23293              * place to the top of element!
23294              *
23295              */
23296             
23297             this.picker().addClass('top');
23298             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23299             
23300             return;
23301         }
23302         
23303         this.picker().addClass('bottom');
23304         
23305         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23306     },
23307     
23308     parseDate : function(value)
23309     {
23310         if(!value || value instanceof Date){
23311             return value;
23312         }
23313         var v = Date.parseDate(value, this.format);
23314         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23315             v = Date.parseDate(value, 'Y-m-d');
23316         }
23317         if(!v && this.altFormats){
23318             if(!this.altFormatsArray){
23319                 this.altFormatsArray = this.altFormats.split("|");
23320             }
23321             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23322                 v = Date.parseDate(value, this.altFormatsArray[i]);
23323             }
23324         }
23325         return v;
23326     },
23327     
23328     formatDate : function(date, fmt)
23329     {   
23330         return (!date || !(date instanceof Date)) ?
23331         date : date.dateFormat(fmt || this.format);
23332     },
23333     
23334     onFocus : function()
23335     {
23336         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23337         this.showPopup();
23338     },
23339     
23340     onBlur : function()
23341     {
23342         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23343         
23344         var d = this.inputEl().getValue();
23345         
23346         this.setValue(d);
23347                 
23348         this.hidePopup();
23349     },
23350     
23351     showPopup : function()
23352     {
23353         this.picker().show();
23354         this.update();
23355         this.place();
23356         
23357         this.fireEvent('showpopup', this, this.date);
23358     },
23359     
23360     hidePopup : function()
23361     {
23362         if(this.isInline) {
23363             return;
23364         }
23365         this.picker().hide();
23366         this.viewMode = this.startViewMode;
23367         this.showMode();
23368         
23369         this.fireEvent('hidepopup', this, this.date);
23370         
23371     },
23372     
23373     onMousedown: function(e)
23374     {
23375         e.stopPropagation();
23376         e.preventDefault();
23377     },
23378     
23379     keyup: function(e)
23380     {
23381         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23382         this.update();
23383     },
23384
23385     setValue: function(v)
23386     {
23387         if(this.fireEvent('beforeselect', this, v) !== false){
23388             var d = new Date(this.parseDate(v) ).clearTime();
23389         
23390             if(isNaN(d.getTime())){
23391                 this.date = this.viewDate = '';
23392                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23393                 return;
23394             }
23395
23396             v = this.formatDate(d);
23397
23398             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23399
23400             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23401
23402             this.update();
23403
23404             this.fireEvent('select', this, this.date);
23405         }
23406     },
23407     
23408     getValue: function()
23409     {
23410         return this.formatDate(this.date);
23411     },
23412     
23413     fireKey: function(e)
23414     {
23415         if (!this.picker().isVisible()){
23416             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23417                 this.showPopup();
23418             }
23419             return;
23420         }
23421         
23422         var dateChanged = false,
23423         dir, day, month,
23424         newDate, newViewDate;
23425         
23426         switch(e.keyCode){
23427             case 27: // escape
23428                 this.hidePopup();
23429                 e.preventDefault();
23430                 break;
23431             case 37: // left
23432             case 39: // right
23433                 if (!this.keyboardNavigation) {
23434                     break;
23435                 }
23436                 dir = e.keyCode == 37 ? -1 : 1;
23437                 
23438                 if (e.ctrlKey){
23439                     newDate = this.moveYear(this.date, dir);
23440                     newViewDate = this.moveYear(this.viewDate, dir);
23441                 } else if (e.shiftKey){
23442                     newDate = this.moveMonth(this.date, dir);
23443                     newViewDate = this.moveMonth(this.viewDate, dir);
23444                 } else {
23445                     newDate = new Date(this.date);
23446                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23447                     newViewDate = new Date(this.viewDate);
23448                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23449                 }
23450                 if (this.dateWithinRange(newDate)){
23451                     this.date = newDate;
23452                     this.viewDate = newViewDate;
23453                     this.setValue(this.formatDate(this.date));
23454 //                    this.update();
23455                     e.preventDefault();
23456                     dateChanged = true;
23457                 }
23458                 break;
23459             case 38: // up
23460             case 40: // down
23461                 if (!this.keyboardNavigation) {
23462                     break;
23463                 }
23464                 dir = e.keyCode == 38 ? -1 : 1;
23465                 if (e.ctrlKey){
23466                     newDate = this.moveYear(this.date, dir);
23467                     newViewDate = this.moveYear(this.viewDate, dir);
23468                 } else if (e.shiftKey){
23469                     newDate = this.moveMonth(this.date, dir);
23470                     newViewDate = this.moveMonth(this.viewDate, dir);
23471                 } else {
23472                     newDate = new Date(this.date);
23473                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23474                     newViewDate = new Date(this.viewDate);
23475                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23476                 }
23477                 if (this.dateWithinRange(newDate)){
23478                     this.date = newDate;
23479                     this.viewDate = newViewDate;
23480                     this.setValue(this.formatDate(this.date));
23481 //                    this.update();
23482                     e.preventDefault();
23483                     dateChanged = true;
23484                 }
23485                 break;
23486             case 13: // enter
23487                 this.setValue(this.formatDate(this.date));
23488                 this.hidePopup();
23489                 e.preventDefault();
23490                 break;
23491             case 9: // tab
23492                 this.setValue(this.formatDate(this.date));
23493                 this.hidePopup();
23494                 break;
23495             case 16: // shift
23496             case 17: // ctrl
23497             case 18: // alt
23498                 break;
23499             default :
23500                 this.hidePopup();
23501                 
23502         }
23503     },
23504     
23505     
23506     onClick: function(e) 
23507     {
23508         e.stopPropagation();
23509         e.preventDefault();
23510         
23511         var target = e.getTarget();
23512         
23513         if(target.nodeName.toLowerCase() === 'i'){
23514             target = Roo.get(target).dom.parentNode;
23515         }
23516         
23517         var nodeName = target.nodeName;
23518         var className = target.className;
23519         var html = target.innerHTML;
23520         //Roo.log(nodeName);
23521         
23522         switch(nodeName.toLowerCase()) {
23523             case 'th':
23524                 switch(className) {
23525                     case 'switch':
23526                         this.showMode(1);
23527                         break;
23528                     case 'prev':
23529                     case 'next':
23530                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23531                         switch(this.viewMode){
23532                                 case 0:
23533                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23534                                         break;
23535                                 case 1:
23536                                 case 2:
23537                                         this.viewDate = this.moveYear(this.viewDate, dir);
23538                                         break;
23539                         }
23540                         this.fill();
23541                         break;
23542                     case 'today':
23543                         var date = new Date();
23544                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23545 //                        this.fill()
23546                         this.setValue(this.formatDate(this.date));
23547                         
23548                         this.hidePopup();
23549                         break;
23550                 }
23551                 break;
23552             case 'span':
23553                 if (className.indexOf('disabled') < 0) {
23554                 if (!this.viewDate) {
23555                     this.viewDate = new Date();
23556                 }
23557                 this.viewDate.setUTCDate(1);
23558                     if (className.indexOf('month') > -1) {
23559                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23560                     } else {
23561                         var year = parseInt(html, 10) || 0;
23562                         this.viewDate.setUTCFullYear(year);
23563                         
23564                     }
23565                     
23566                     if(this.singleMode){
23567                         this.setValue(this.formatDate(this.viewDate));
23568                         this.hidePopup();
23569                         return;
23570                     }
23571                     
23572                     this.showMode(-1);
23573                     this.fill();
23574                 }
23575                 break;
23576                 
23577             case 'td':
23578                 //Roo.log(className);
23579                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23580                     var day = parseInt(html, 10) || 1;
23581                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23582                         month = (this.viewDate || new Date()).getUTCMonth();
23583
23584                     if (className.indexOf('old') > -1) {
23585                         if(month === 0 ){
23586                             month = 11;
23587                             year -= 1;
23588                         }else{
23589                             month -= 1;
23590                         }
23591                     } else if (className.indexOf('new') > -1) {
23592                         if (month == 11) {
23593                             month = 0;
23594                             year += 1;
23595                         } else {
23596                             month += 1;
23597                         }
23598                     }
23599                     //Roo.log([year,month,day]);
23600                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23601                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23602 //                    this.fill();
23603                     //Roo.log(this.formatDate(this.date));
23604                     this.setValue(this.formatDate(this.date));
23605                     this.hidePopup();
23606                 }
23607                 break;
23608         }
23609     },
23610     
23611     setStartDate: function(startDate)
23612     {
23613         this.startDate = startDate || -Infinity;
23614         if (this.startDate !== -Infinity) {
23615             this.startDate = this.parseDate(this.startDate);
23616         }
23617         this.update();
23618         this.updateNavArrows();
23619     },
23620
23621     setEndDate: function(endDate)
23622     {
23623         this.endDate = endDate || Infinity;
23624         if (this.endDate !== Infinity) {
23625             this.endDate = this.parseDate(this.endDate);
23626         }
23627         this.update();
23628         this.updateNavArrows();
23629     },
23630     
23631     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23632     {
23633         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23634         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23635             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23636         }
23637         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23638             return parseInt(d, 10);
23639         });
23640         this.update();
23641         this.updateNavArrows();
23642     },
23643     
23644     updateNavArrows: function() 
23645     {
23646         if(this.singleMode){
23647             return;
23648         }
23649         
23650         var d = new Date(this.viewDate),
23651         year = d.getUTCFullYear(),
23652         month = d.getUTCMonth();
23653         
23654         Roo.each(this.picker().select('.prev', true).elements, function(v){
23655             v.show();
23656             switch (this.viewMode) {
23657                 case 0:
23658
23659                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23660                         v.hide();
23661                     }
23662                     break;
23663                 case 1:
23664                 case 2:
23665                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23666                         v.hide();
23667                     }
23668                     break;
23669             }
23670         });
23671         
23672         Roo.each(this.picker().select('.next', true).elements, function(v){
23673             v.show();
23674             switch (this.viewMode) {
23675                 case 0:
23676
23677                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23678                         v.hide();
23679                     }
23680                     break;
23681                 case 1:
23682                 case 2:
23683                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23684                         v.hide();
23685                     }
23686                     break;
23687             }
23688         })
23689     },
23690     
23691     moveMonth: function(date, dir)
23692     {
23693         if (!dir) {
23694             return date;
23695         }
23696         var new_date = new Date(date.valueOf()),
23697         day = new_date.getUTCDate(),
23698         month = new_date.getUTCMonth(),
23699         mag = Math.abs(dir),
23700         new_month, test;
23701         dir = dir > 0 ? 1 : -1;
23702         if (mag == 1){
23703             test = dir == -1
23704             // If going back one month, make sure month is not current month
23705             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23706             ? function(){
23707                 return new_date.getUTCMonth() == month;
23708             }
23709             // If going forward one month, make sure month is as expected
23710             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23711             : function(){
23712                 return new_date.getUTCMonth() != new_month;
23713             };
23714             new_month = month + dir;
23715             new_date.setUTCMonth(new_month);
23716             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23717             if (new_month < 0 || new_month > 11) {
23718                 new_month = (new_month + 12) % 12;
23719             }
23720         } else {
23721             // For magnitudes >1, move one month at a time...
23722             for (var i=0; i<mag; i++) {
23723                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23724                 new_date = this.moveMonth(new_date, dir);
23725             }
23726             // ...then reset the day, keeping it in the new month
23727             new_month = new_date.getUTCMonth();
23728             new_date.setUTCDate(day);
23729             test = function(){
23730                 return new_month != new_date.getUTCMonth();
23731             };
23732         }
23733         // Common date-resetting loop -- if date is beyond end of month, make it
23734         // end of month
23735         while (test()){
23736             new_date.setUTCDate(--day);
23737             new_date.setUTCMonth(new_month);
23738         }
23739         return new_date;
23740     },
23741
23742     moveYear: function(date, dir)
23743     {
23744         return this.moveMonth(date, dir*12);
23745     },
23746
23747     dateWithinRange: function(date)
23748     {
23749         return date >= this.startDate && date <= this.endDate;
23750     },
23751
23752     
23753     remove: function() 
23754     {
23755         this.picker().remove();
23756     },
23757     
23758     validateValue : function(value)
23759     {
23760         if(this.getVisibilityEl().hasClass('hidden')){
23761             return true;
23762         }
23763         
23764         if(value.length < 1)  {
23765             if(this.allowBlank){
23766                 return true;
23767             }
23768             return false;
23769         }
23770         
23771         if(value.length < this.minLength){
23772             return false;
23773         }
23774         if(value.length > this.maxLength){
23775             return false;
23776         }
23777         if(this.vtype){
23778             var vt = Roo.form.VTypes;
23779             if(!vt[this.vtype](value, this)){
23780                 return false;
23781             }
23782         }
23783         if(typeof this.validator == "function"){
23784             var msg = this.validator(value);
23785             if(msg !== true){
23786                 return false;
23787             }
23788         }
23789         
23790         if(this.regex && !this.regex.test(value)){
23791             return false;
23792         }
23793         
23794         if(typeof(this.parseDate(value)) == 'undefined'){
23795             return false;
23796         }
23797         
23798         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23799             return false;
23800         }      
23801         
23802         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23803             return false;
23804         } 
23805         
23806         
23807         return true;
23808     },
23809     
23810     reset : function()
23811     {
23812         this.date = this.viewDate = '';
23813         
23814         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23815     }
23816    
23817 });
23818
23819 Roo.apply(Roo.bootstrap.form.DateField,  {
23820     
23821     head : {
23822         tag: 'thead',
23823         cn: [
23824         {
23825             tag: 'tr',
23826             cn: [
23827             {
23828                 tag: 'th',
23829                 cls: 'prev',
23830                 html: '<i class="fa fa-arrow-left"/>'
23831             },
23832             {
23833                 tag: 'th',
23834                 cls: 'switch',
23835                 colspan: '5'
23836             },
23837             {
23838                 tag: 'th',
23839                 cls: 'next',
23840                 html: '<i class="fa fa-arrow-right"/>'
23841             }
23842
23843             ]
23844         }
23845         ]
23846     },
23847     
23848     content : {
23849         tag: 'tbody',
23850         cn: [
23851         {
23852             tag: 'tr',
23853             cn: [
23854             {
23855                 tag: 'td',
23856                 colspan: '7'
23857             }
23858             ]
23859         }
23860         ]
23861     },
23862     
23863     footer : {
23864         tag: 'tfoot',
23865         cn: [
23866         {
23867             tag: 'tr',
23868             cn: [
23869             {
23870                 tag: 'th',
23871                 colspan: '7',
23872                 cls: 'today'
23873             }
23874                     
23875             ]
23876         }
23877         ]
23878     },
23879     
23880     dates:{
23881         en: {
23882             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23883             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23884             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23885             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23886             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23887             today: "Today"
23888         }
23889     },
23890     
23891     modes: [
23892     {
23893         clsName: 'days',
23894         navFnc: 'Month',
23895         navStep: 1
23896     },
23897     {
23898         clsName: 'months',
23899         navFnc: 'FullYear',
23900         navStep: 1
23901     },
23902     {
23903         clsName: 'years',
23904         navFnc: 'FullYear',
23905         navStep: 10
23906     }]
23907 });
23908
23909 Roo.apply(Roo.bootstrap.form.DateField,  {
23910   
23911     template : {
23912         tag: 'div',
23913         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23914         cn: [
23915         {
23916             tag: 'div',
23917             cls: 'datepicker-days',
23918             cn: [
23919             {
23920                 tag: 'table',
23921                 cls: 'table-condensed',
23922                 cn:[
23923                 Roo.bootstrap.form.DateField.head,
23924                 {
23925                     tag: 'tbody'
23926                 },
23927                 Roo.bootstrap.form.DateField.footer
23928                 ]
23929             }
23930             ]
23931         },
23932         {
23933             tag: 'div',
23934             cls: 'datepicker-months',
23935             cn: [
23936             {
23937                 tag: 'table',
23938                 cls: 'table-condensed',
23939                 cn:[
23940                 Roo.bootstrap.form.DateField.head,
23941                 Roo.bootstrap.form.DateField.content,
23942                 Roo.bootstrap.form.DateField.footer
23943                 ]
23944             }
23945             ]
23946         },
23947         {
23948             tag: 'div',
23949             cls: 'datepicker-years',
23950             cn: [
23951             {
23952                 tag: 'table',
23953                 cls: 'table-condensed',
23954                 cn:[
23955                 Roo.bootstrap.form.DateField.head,
23956                 Roo.bootstrap.form.DateField.content,
23957                 Roo.bootstrap.form.DateField.footer
23958                 ]
23959             }
23960             ]
23961         }
23962         ]
23963     }
23964 });
23965
23966  
23967
23968  /*
23969  * - LGPL
23970  *
23971  * TimeField
23972  * 
23973  */
23974
23975 /**
23976  * @class Roo.bootstrap.form.TimeField
23977  * @extends Roo.bootstrap.form.Input
23978  * Bootstrap DateField class
23979  * 
23980  * 
23981  * @constructor
23982  * Create a new TimeField
23983  * @param {Object} config The config object
23984  */
23985
23986 Roo.bootstrap.form.TimeField = function(config){
23987     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23988     this.addEvents({
23989             /**
23990              * @event show
23991              * Fires when this field show.
23992              * @param {Roo.bootstrap.form.DateField} thisthis
23993              * @param {Mixed} date The date value
23994              */
23995             show : true,
23996             /**
23997              * @event show
23998              * Fires when this field hide.
23999              * @param {Roo.bootstrap.form.DateField} this
24000              * @param {Mixed} date The date value
24001              */
24002             hide : true,
24003             /**
24004              * @event select
24005              * Fires when select a date.
24006              * @param {Roo.bootstrap.form.DateField} this
24007              * @param {Mixed} date The date value
24008              */
24009             select : true
24010         });
24011 };
24012
24013 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
24014     
24015     /**
24016      * @cfg {String} format
24017      * The default time format string which can be overriden for localization support.  The format must be
24018      * valid according to {@link Date#parseDate} (defaults to 'H:i').
24019      */
24020     format : "H:i",
24021
24022     getAutoCreate : function()
24023     {
24024         this.after = '<i class="fa far fa-clock"></i>';
24025         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
24026         
24027          
24028     },
24029     onRender: function(ct, position)
24030     {
24031         
24032         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24033                 
24034         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24035         
24036         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24037         
24038         this.pop = this.picker().select('>.datepicker-time',true).first();
24039         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24040         
24041         this.picker().on('mousedown', this.onMousedown, this);
24042         this.picker().on('click', this.onClick, this);
24043         
24044         this.picker().addClass('datepicker-dropdown');
24045     
24046         this.fillTime();
24047         this.update();
24048             
24049         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24050         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24051         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24052         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24053         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24054         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24055
24056     },
24057     
24058     fireKey: function(e){
24059         if (!this.picker().isVisible()){
24060             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24061                 this.show();
24062             }
24063             return;
24064         }
24065
24066         e.preventDefault();
24067         
24068         switch(e.keyCode){
24069             case 27: // escape
24070                 this.hide();
24071                 break;
24072             case 37: // left
24073             case 39: // right
24074                 this.onTogglePeriod();
24075                 break;
24076             case 38: // up
24077                 this.onIncrementMinutes();
24078                 break;
24079             case 40: // down
24080                 this.onDecrementMinutes();
24081                 break;
24082             case 13: // enter
24083             case 9: // tab
24084                 this.setTime();
24085                 break;
24086         }
24087     },
24088     
24089     onClick: function(e) {
24090         e.stopPropagation();
24091         e.preventDefault();
24092     },
24093     
24094     picker : function()
24095     {
24096         return this.pickerEl;
24097     },
24098     
24099     fillTime: function()
24100     {    
24101         var time = this.pop.select('tbody', true).first();
24102         
24103         time.dom.innerHTML = '';
24104         
24105         time.createChild({
24106             tag: 'tr',
24107             cn: [
24108                 {
24109                     tag: 'td',
24110                     cn: [
24111                         {
24112                             tag: 'a',
24113                             href: '#',
24114                             cls: 'btn',
24115                             cn: [
24116                                 {
24117                                     tag: 'i',
24118                                     cls: 'hours-up fa fas fa-chevron-up'
24119                                 }
24120                             ]
24121                         } 
24122                     ]
24123                 },
24124                 {
24125                     tag: 'td',
24126                     cls: 'separator'
24127                 },
24128                 {
24129                     tag: 'td',
24130                     cn: [
24131                         {
24132                             tag: 'a',
24133                             href: '#',
24134                             cls: 'btn',
24135                             cn: [
24136                                 {
24137                                     tag: 'i',
24138                                     cls: 'minutes-up fa fas fa-chevron-up'
24139                                 }
24140                             ]
24141                         }
24142                     ]
24143                 },
24144                 {
24145                     tag: 'td',
24146                     cls: 'separator'
24147                 }
24148             ]
24149         });
24150         
24151         time.createChild({
24152             tag: 'tr',
24153             cn: [
24154                 {
24155                     tag: 'td',
24156                     cn: [
24157                         {
24158                             tag: 'span',
24159                             cls: 'timepicker-hour',
24160                             html: '00'
24161                         }  
24162                     ]
24163                 },
24164                 {
24165                     tag: 'td',
24166                     cls: 'separator',
24167                     html: ':'
24168                 },
24169                 {
24170                     tag: 'td',
24171                     cn: [
24172                         {
24173                             tag: 'span',
24174                             cls: 'timepicker-minute',
24175                             html: '00'
24176                         }  
24177                     ]
24178                 },
24179                 {
24180                     tag: 'td',
24181                     cls: 'separator'
24182                 },
24183                 {
24184                     tag: 'td',
24185                     cn: [
24186                         {
24187                             tag: 'button',
24188                             type: 'button',
24189                             cls: 'btn btn-primary period',
24190                             html: 'AM'
24191                             
24192                         }
24193                     ]
24194                 }
24195             ]
24196         });
24197         
24198         time.createChild({
24199             tag: 'tr',
24200             cn: [
24201                 {
24202                     tag: 'td',
24203                     cn: [
24204                         {
24205                             tag: 'a',
24206                             href: '#',
24207                             cls: 'btn',
24208                             cn: [
24209                                 {
24210                                     tag: 'span',
24211                                     cls: 'hours-down fa fas fa-chevron-down'
24212                                 }
24213                             ]
24214                         }
24215                     ]
24216                 },
24217                 {
24218                     tag: 'td',
24219                     cls: 'separator'
24220                 },
24221                 {
24222                     tag: 'td',
24223                     cn: [
24224                         {
24225                             tag: 'a',
24226                             href: '#',
24227                             cls: 'btn',
24228                             cn: [
24229                                 {
24230                                     tag: 'span',
24231                                     cls: 'minutes-down fa fas fa-chevron-down'
24232                                 }
24233                             ]
24234                         }
24235                     ]
24236                 },
24237                 {
24238                     tag: 'td',
24239                     cls: 'separator'
24240                 }
24241             ]
24242         });
24243         
24244     },
24245     
24246     update: function()
24247     {
24248         
24249         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24250         
24251         this.fill();
24252     },
24253     
24254     fill: function() 
24255     {
24256         var hours = this.time.getHours();
24257         var minutes = this.time.getMinutes();
24258         var period = 'AM';
24259         
24260         if(hours > 11){
24261             period = 'PM';
24262         }
24263         
24264         if(hours == 0){
24265             hours = 12;
24266         }
24267         
24268         
24269         if(hours > 12){
24270             hours = hours - 12;
24271         }
24272         
24273         if(hours < 10){
24274             hours = '0' + hours;
24275         }
24276         
24277         if(minutes < 10){
24278             minutes = '0' + minutes;
24279         }
24280         
24281         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24282         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24283         this.pop.select('button', true).first().dom.innerHTML = period;
24284         
24285     },
24286     
24287     place: function()
24288     {   
24289         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24290         
24291         var cls = ['bottom'];
24292         
24293         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24294             cls.pop();
24295             cls.push('top');
24296         }
24297         
24298         cls.push('right');
24299         
24300         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24301             cls.pop();
24302             cls.push('left');
24303         }
24304         //this.picker().setXY(20000,20000);
24305         this.picker().addClass(cls.join('-'));
24306         
24307         var _this = this;
24308         
24309         Roo.each(cls, function(c){
24310             if(c == 'bottom'){
24311                 (function() {
24312                  //  
24313                 }).defer(200);
24314                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24315                 //_this.picker().setTop(_this.inputEl().getHeight());
24316                 return;
24317             }
24318             if(c == 'top'){
24319                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24320                 
24321                 //_this.picker().setTop(0 - _this.picker().getHeight());
24322                 return;
24323             }
24324             /*
24325             if(c == 'left'){
24326                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24327                 return;
24328             }
24329             if(c == 'right'){
24330                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24331                 return;
24332             }
24333             */
24334         });
24335         
24336     },
24337   
24338     onFocus : function()
24339     {
24340         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24341         this.show();
24342     },
24343     
24344     onBlur : function()
24345     {
24346         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24347         this.hide();
24348     },
24349     
24350     show : function()
24351     {
24352         this.picker().show();
24353         this.pop.show();
24354         this.update();
24355         this.place();
24356         
24357         this.fireEvent('show', this, this.date);
24358     },
24359     
24360     hide : function()
24361     {
24362         this.picker().hide();
24363         this.pop.hide();
24364         
24365         this.fireEvent('hide', this, this.date);
24366     },
24367     
24368     setTime : function()
24369     {
24370         this.hide();
24371         this.setValue(this.time.format(this.format));
24372         
24373         this.fireEvent('select', this, this.date);
24374         
24375         
24376     },
24377     
24378     onMousedown: function(e){
24379         e.stopPropagation();
24380         e.preventDefault();
24381     },
24382     
24383     onIncrementHours: function()
24384     {
24385         Roo.log('onIncrementHours');
24386         this.time = this.time.add(Date.HOUR, 1);
24387         this.update();
24388         
24389     },
24390     
24391     onDecrementHours: function()
24392     {
24393         Roo.log('onDecrementHours');
24394         this.time = this.time.add(Date.HOUR, -1);
24395         this.update();
24396     },
24397     
24398     onIncrementMinutes: function()
24399     {
24400         Roo.log('onIncrementMinutes');
24401         this.time = this.time.add(Date.MINUTE, 1);
24402         this.update();
24403     },
24404     
24405     onDecrementMinutes: function()
24406     {
24407         Roo.log('onDecrementMinutes');
24408         this.time = this.time.add(Date.MINUTE, -1);
24409         this.update();
24410     },
24411     
24412     onTogglePeriod: function()
24413     {
24414         Roo.log('onTogglePeriod');
24415         this.time = this.time.add(Date.HOUR, 12);
24416         this.update();
24417     }
24418     
24419    
24420 });
24421  
24422
24423 Roo.apply(Roo.bootstrap.form.TimeField,  {
24424   
24425     template : {
24426         tag: 'div',
24427         cls: 'datepicker dropdown-menu',
24428         cn: [
24429             {
24430                 tag: 'div',
24431                 cls: 'datepicker-time',
24432                 cn: [
24433                 {
24434                     tag: 'table',
24435                     cls: 'table-condensed',
24436                     cn:[
24437                         {
24438                             tag: 'tbody',
24439                             cn: [
24440                                 {
24441                                     tag: 'tr',
24442                                     cn: [
24443                                     {
24444                                         tag: 'td',
24445                                         colspan: '7'
24446                                     }
24447                                     ]
24448                                 }
24449                             ]
24450                         },
24451                         {
24452                             tag: 'tfoot',
24453                             cn: [
24454                                 {
24455                                     tag: 'tr',
24456                                     cn: [
24457                                     {
24458                                         tag: 'th',
24459                                         colspan: '7',
24460                                         cls: '',
24461                                         cn: [
24462                                             {
24463                                                 tag: 'button',
24464                                                 cls: 'btn btn-info ok',
24465                                                 html: 'OK'
24466                                             }
24467                                         ]
24468                                     }
24469                     
24470                                     ]
24471                                 }
24472                             ]
24473                         }
24474                     ]
24475                 }
24476                 ]
24477             }
24478         ]
24479     }
24480 });
24481
24482  
24483
24484  /*
24485  * - LGPL
24486  *
24487  * MonthField
24488  * 
24489  */
24490
24491 /**
24492  * @class Roo.bootstrap.form.MonthField
24493  * @extends Roo.bootstrap.form.Input
24494  * Bootstrap MonthField class
24495  * 
24496  * @cfg {String} language default en
24497  * 
24498  * @constructor
24499  * Create a new MonthField
24500  * @param {Object} config The config object
24501  */
24502
24503 Roo.bootstrap.form.MonthField = function(config){
24504     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24505     
24506     this.addEvents({
24507         /**
24508          * @event show
24509          * Fires when this field show.
24510          * @param {Roo.bootstrap.form.MonthField} this
24511          * @param {Mixed} date The date value
24512          */
24513         show : true,
24514         /**
24515          * @event show
24516          * Fires when this field hide.
24517          * @param {Roo.bootstrap.form.MonthField} this
24518          * @param {Mixed} date The date value
24519          */
24520         hide : true,
24521         /**
24522          * @event select
24523          * Fires when select a date.
24524          * @param {Roo.bootstrap.form.MonthField} this
24525          * @param {String} oldvalue The old value
24526          * @param {String} newvalue The new value
24527          */
24528         select : true
24529     });
24530 };
24531
24532 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24533     
24534     onRender: function(ct, position)
24535     {
24536         
24537         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24538         
24539         this.language = this.language || 'en';
24540         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24541         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24542         
24543         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24544         this.isInline = false;
24545         this.isInput = true;
24546         this.component = this.el.select('.add-on', true).first() || false;
24547         this.component = (this.component && this.component.length === 0) ? false : this.component;
24548         this.hasInput = this.component && this.inputEL().length;
24549         
24550         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24551         
24552         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24553         
24554         this.picker().on('mousedown', this.onMousedown, this);
24555         this.picker().on('click', this.onClick, this);
24556         
24557         this.picker().addClass('datepicker-dropdown');
24558         
24559         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24560             v.setStyle('width', '189px');
24561         });
24562         
24563         this.fillMonths();
24564         
24565         this.update();
24566         
24567         if(this.isInline) {
24568             this.show();
24569         }
24570         
24571     },
24572     
24573     setValue: function(v, suppressEvent)
24574     {   
24575         var o = this.getValue();
24576         
24577         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24578         
24579         this.update();
24580
24581         if(suppressEvent !== true){
24582             this.fireEvent('select', this, o, v);
24583         }
24584         
24585     },
24586     
24587     getValue: function()
24588     {
24589         return this.value;
24590     },
24591     
24592     onClick: function(e) 
24593     {
24594         e.stopPropagation();
24595         e.preventDefault();
24596         
24597         var target = e.getTarget();
24598         
24599         if(target.nodeName.toLowerCase() === 'i'){
24600             target = Roo.get(target).dom.parentNode;
24601         }
24602         
24603         var nodeName = target.nodeName;
24604         var className = target.className;
24605         var html = target.innerHTML;
24606         
24607         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24608             return;
24609         }
24610         
24611         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24612         
24613         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24614         
24615         this.hide();
24616                         
24617     },
24618     
24619     picker : function()
24620     {
24621         return this.pickerEl;
24622     },
24623     
24624     fillMonths: function()
24625     {    
24626         var i = 0;
24627         var months = this.picker().select('>.datepicker-months td', true).first();
24628         
24629         months.dom.innerHTML = '';
24630         
24631         while (i < 12) {
24632             var month = {
24633                 tag: 'span',
24634                 cls: 'month',
24635                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24636             };
24637             
24638             months.createChild(month);
24639         }
24640         
24641     },
24642     
24643     update: function()
24644     {
24645         var _this = this;
24646         
24647         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24648             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24649         }
24650         
24651         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24652             e.removeClass('active');
24653             
24654             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24655                 e.addClass('active');
24656             }
24657         })
24658     },
24659     
24660     place: function()
24661     {
24662         if(this.isInline) {
24663             return;
24664         }
24665         
24666         this.picker().removeClass(['bottom', 'top']);
24667         
24668         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24669             /*
24670              * place to the top of element!
24671              *
24672              */
24673             
24674             this.picker().addClass('top');
24675             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24676             
24677             return;
24678         }
24679         
24680         this.picker().addClass('bottom');
24681         
24682         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24683     },
24684     
24685     onFocus : function()
24686     {
24687         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24688         this.show();
24689     },
24690     
24691     onBlur : function()
24692     {
24693         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24694         
24695         var d = this.inputEl().getValue();
24696         
24697         this.setValue(d);
24698                 
24699         this.hide();
24700     },
24701     
24702     show : function()
24703     {
24704         this.picker().show();
24705         this.picker().select('>.datepicker-months', true).first().show();
24706         this.update();
24707         this.place();
24708         
24709         this.fireEvent('show', this, this.date);
24710     },
24711     
24712     hide : function()
24713     {
24714         if(this.isInline) {
24715             return;
24716         }
24717         this.picker().hide();
24718         this.fireEvent('hide', this, this.date);
24719         
24720     },
24721     
24722     onMousedown: function(e)
24723     {
24724         e.stopPropagation();
24725         e.preventDefault();
24726     },
24727     
24728     keyup: function(e)
24729     {
24730         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24731         this.update();
24732     },
24733
24734     fireKey: function(e)
24735     {
24736         if (!this.picker().isVisible()){
24737             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24738                 this.show();
24739             }
24740             return;
24741         }
24742         
24743         var dir;
24744         
24745         switch(e.keyCode){
24746             case 27: // escape
24747                 this.hide();
24748                 e.preventDefault();
24749                 break;
24750             case 37: // left
24751             case 39: // right
24752                 dir = e.keyCode == 37 ? -1 : 1;
24753                 
24754                 this.vIndex = this.vIndex + dir;
24755                 
24756                 if(this.vIndex < 0){
24757                     this.vIndex = 0;
24758                 }
24759                 
24760                 if(this.vIndex > 11){
24761                     this.vIndex = 11;
24762                 }
24763                 
24764                 if(isNaN(this.vIndex)){
24765                     this.vIndex = 0;
24766                 }
24767                 
24768                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24769                 
24770                 break;
24771             case 38: // up
24772             case 40: // down
24773                 
24774                 dir = e.keyCode == 38 ? -1 : 1;
24775                 
24776                 this.vIndex = this.vIndex + dir * 4;
24777                 
24778                 if(this.vIndex < 0){
24779                     this.vIndex = 0;
24780                 }
24781                 
24782                 if(this.vIndex > 11){
24783                     this.vIndex = 11;
24784                 }
24785                 
24786                 if(isNaN(this.vIndex)){
24787                     this.vIndex = 0;
24788                 }
24789                 
24790                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24791                 break;
24792                 
24793             case 13: // enter
24794                 
24795                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24796                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24797                 }
24798                 
24799                 this.hide();
24800                 e.preventDefault();
24801                 break;
24802             case 9: // tab
24803                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24804                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24805                 }
24806                 this.hide();
24807                 break;
24808             case 16: // shift
24809             case 17: // ctrl
24810             case 18: // alt
24811                 break;
24812             default :
24813                 this.hide();
24814                 
24815         }
24816     },
24817     
24818     remove: function() 
24819     {
24820         this.picker().remove();
24821     }
24822    
24823 });
24824
24825 Roo.apply(Roo.bootstrap.form.MonthField,  {
24826     
24827     content : {
24828         tag: 'tbody',
24829         cn: [
24830         {
24831             tag: 'tr',
24832             cn: [
24833             {
24834                 tag: 'td',
24835                 colspan: '7'
24836             }
24837             ]
24838         }
24839         ]
24840     },
24841     
24842     dates:{
24843         en: {
24844             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24845             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24846         }
24847     }
24848 });
24849
24850 Roo.apply(Roo.bootstrap.form.MonthField,  {
24851   
24852     template : {
24853         tag: 'div',
24854         cls: 'datepicker dropdown-menu roo-dynamic',
24855         cn: [
24856             {
24857                 tag: 'div',
24858                 cls: 'datepicker-months',
24859                 cn: [
24860                 {
24861                     tag: 'table',
24862                     cls: 'table-condensed',
24863                     cn:[
24864                         Roo.bootstrap.form.DateField.content
24865                     ]
24866                 }
24867                 ]
24868             }
24869         ]
24870     }
24871 });
24872
24873  
24874
24875  
24876  /*
24877  * - LGPL
24878  *
24879  * CheckBox
24880  * 
24881  */
24882
24883 /**
24884  * @class Roo.bootstrap.form.CheckBox
24885  * @extends Roo.bootstrap.form.Input
24886  * Bootstrap CheckBox class
24887  * 
24888  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24889  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24890  * @cfg {String} boxLabel The text that appears beside the checkbox
24891  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24892  * @cfg {Boolean} checked initnal the element
24893  * @cfg {Boolean} inline inline the element (default false)
24894  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24895  * @cfg {String} tooltip label tooltip
24896  * 
24897  * @constructor
24898  * Create a new CheckBox
24899  * @param {Object} config The config object
24900  */
24901
24902 Roo.bootstrap.form.CheckBox = function(config){
24903     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24904    
24905     this.addEvents({
24906         /**
24907         * @event check
24908         * Fires when the element is checked or unchecked.
24909         * @param {Roo.bootstrap.form.CheckBox} this This input
24910         * @param {Boolean} checked The new checked value
24911         */
24912        check : true,
24913        /**
24914         * @event click
24915         * Fires when the element is click.
24916         * @param {Roo.bootstrap.form.CheckBox} this This input
24917         */
24918        click : true
24919     });
24920     
24921 };
24922
24923 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24924   
24925     inputType: 'checkbox',
24926     inputValue: 1,
24927     valueOff: 0,
24928     boxLabel: false,
24929     checked: false,
24930     weight : false,
24931     inline: false,
24932     tooltip : '',
24933     
24934     // checkbox success does not make any sense really.. 
24935     invalidClass : "",
24936     validClass : "",
24937     
24938     
24939     getAutoCreate : function()
24940     {
24941         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24942         
24943         var id = Roo.id();
24944         
24945         var cfg = {};
24946         
24947         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24948         
24949         if(this.inline){
24950             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24951         }
24952         
24953         var input =  {
24954             tag: 'input',
24955             id : id,
24956             type : this.inputType,
24957             value : this.inputValue,
24958             cls : 'roo-' + this.inputType, //'form-box',
24959             placeholder : this.placeholder || ''
24960             
24961         };
24962         
24963         if(this.inputType != 'radio'){
24964             var hidden =  {
24965                 tag: 'input',
24966                 type : 'hidden',
24967                 cls : 'roo-hidden-value',
24968                 value : this.checked ? this.inputValue : this.valueOff
24969             };
24970         }
24971         
24972             
24973         if (this.weight) { // Validity check?
24974             cfg.cls += " " + this.inputType + "-" + this.weight;
24975         }
24976         
24977         if (this.disabled) {
24978             input.disabled=true;
24979         }
24980         
24981         if(this.checked){
24982             input.checked = this.checked;
24983         }
24984         
24985         if (this.name) {
24986             
24987             input.name = this.name;
24988             
24989             if(this.inputType != 'radio'){
24990                 hidden.name = this.name;
24991                 input.name = '_hidden_' + this.name;
24992             }
24993         }
24994         
24995         if (this.size) {
24996             input.cls += ' input-' + this.size;
24997         }
24998         
24999         var settings=this;
25000         
25001         ['xs','sm','md','lg'].map(function(size){
25002             if (settings[size]) {
25003                 cfg.cls += ' col-' + size + '-' + settings[size];
25004             }
25005         });
25006         
25007         var inputblock = input;
25008          
25009         if (this.before || this.after) {
25010             
25011             inputblock = {
25012                 cls : 'input-group',
25013                 cn :  [] 
25014             };
25015             
25016             if (this.before) {
25017                 inputblock.cn.push({
25018                     tag :'span',
25019                     cls : 'input-group-addon',
25020                     html : this.before
25021                 });
25022             }
25023             
25024             inputblock.cn.push(input);
25025             
25026             if(this.inputType != 'radio'){
25027                 inputblock.cn.push(hidden);
25028             }
25029             
25030             if (this.after) {
25031                 inputblock.cn.push({
25032                     tag :'span',
25033                     cls : 'input-group-addon',
25034                     html : this.after
25035                 });
25036             }
25037             
25038         }
25039         var boxLabelCfg = false;
25040         
25041         if(this.boxLabel){
25042            
25043             boxLabelCfg = {
25044                 tag: 'label',
25045                 //'for': id, // box label is handled by onclick - so no for...
25046                 cls: 'box-label',
25047                 html: this.boxLabel
25048             };
25049             if(this.tooltip){
25050                 boxLabelCfg.tooltip = this.tooltip;
25051             }
25052              
25053         }
25054         
25055         
25056         if (align ==='left' && this.fieldLabel.length) {
25057 //                Roo.log("left and has label");
25058             cfg.cn = [
25059                 {
25060                     tag: 'label',
25061                     'for' :  id,
25062                     cls : 'control-label',
25063                     html : this.fieldLabel
25064                 },
25065                 {
25066                     cls : "", 
25067                     cn: [
25068                         inputblock
25069                     ]
25070                 }
25071             ];
25072             
25073             if (boxLabelCfg) {
25074                 cfg.cn[1].cn.push(boxLabelCfg);
25075             }
25076             
25077             if(this.labelWidth > 12){
25078                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25079             }
25080             
25081             if(this.labelWidth < 13 && this.labelmd == 0){
25082                 this.labelmd = this.labelWidth;
25083             }
25084             
25085             if(this.labellg > 0){
25086                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25087                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25088             }
25089             
25090             if(this.labelmd > 0){
25091                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25092                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25093             }
25094             
25095             if(this.labelsm > 0){
25096                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25097                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25098             }
25099             
25100             if(this.labelxs > 0){
25101                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25102                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25103             }
25104             
25105         } else if ( this.fieldLabel.length) {
25106 //                Roo.log(" label");
25107                 cfg.cn = [
25108                    
25109                     {
25110                         tag: this.boxLabel ? 'span' : 'label',
25111                         'for': id,
25112                         cls: 'control-label box-input-label',
25113                         //cls : 'input-group-addon',
25114                         html : this.fieldLabel
25115                     },
25116                     
25117                     inputblock
25118                     
25119                 ];
25120                 if (boxLabelCfg) {
25121                     cfg.cn.push(boxLabelCfg);
25122                 }
25123
25124         } else {
25125             
25126 //                Roo.log(" no label && no align");
25127                 cfg.cn = [  inputblock ] ;
25128                 if (boxLabelCfg) {
25129                     cfg.cn.push(boxLabelCfg);
25130                 }
25131
25132                 
25133         }
25134         
25135        
25136         
25137         if(this.inputType != 'radio'){
25138             cfg.cn.push(hidden);
25139         }
25140         
25141         return cfg;
25142         
25143     },
25144     
25145     /**
25146      * return the real input element.
25147      */
25148     inputEl: function ()
25149     {
25150         return this.el.select('input.roo-' + this.inputType,true).first();
25151     },
25152     hiddenEl: function ()
25153     {
25154         return this.el.select('input.roo-hidden-value',true).first();
25155     },
25156     
25157     labelEl: function()
25158     {
25159         return this.el.select('label.control-label',true).first();
25160     },
25161     /* depricated... */
25162     
25163     label: function()
25164     {
25165         return this.labelEl();
25166     },
25167     
25168     boxLabelEl: function()
25169     {
25170         return this.el.select('label.box-label',true).first();
25171     },
25172     
25173     initEvents : function()
25174     {
25175 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25176         
25177         this.inputEl().on('click', this.onClick,  this);
25178         
25179         if (this.boxLabel) { 
25180             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25181         }
25182         
25183         this.startValue = this.getValue();
25184         
25185         if(this.groupId){
25186             Roo.bootstrap.form.CheckBox.register(this);
25187         }
25188     },
25189     
25190     onClick : function(e)
25191     {   
25192         if(this.fireEvent('click', this, e) !== false){
25193             this.setChecked(!this.checked);
25194         }
25195         
25196     },
25197     
25198     setChecked : function(state,suppressEvent)
25199     {
25200         this.startValue = this.getValue();
25201
25202         if(this.inputType == 'radio'){
25203             
25204             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25205                 e.dom.checked = false;
25206             });
25207             
25208             this.inputEl().dom.checked = true;
25209             
25210             this.inputEl().dom.value = this.inputValue;
25211             
25212             if(suppressEvent !== true){
25213                 this.fireEvent('check', this, true);
25214             }
25215             
25216             this.validate();
25217             
25218             return;
25219         }
25220         
25221         this.checked = state;
25222         
25223         this.inputEl().dom.checked = state;
25224         
25225         
25226         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25227         
25228         if(suppressEvent !== true){
25229             this.fireEvent('check', this, state);
25230         }
25231         
25232         this.validate();
25233     },
25234     
25235     getValue : function()
25236     {
25237         if(this.inputType == 'radio'){
25238             return this.getGroupValue();
25239         }
25240         
25241         return this.hiddenEl().dom.value;
25242         
25243     },
25244     
25245     getGroupValue : function()
25246     {
25247         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25248             return '';
25249         }
25250         
25251         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25252     },
25253     
25254     setValue : function(v,suppressEvent)
25255     {
25256         if(this.inputType == 'radio'){
25257             this.setGroupValue(v, suppressEvent);
25258             return;
25259         }
25260         
25261         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25262         
25263         this.validate();
25264     },
25265     
25266     setGroupValue : function(v, suppressEvent)
25267     {
25268         this.startValue = this.getValue();
25269         
25270         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25271             e.dom.checked = false;
25272             
25273             if(e.dom.value == v){
25274                 e.dom.checked = true;
25275             }
25276         });
25277         
25278         if(suppressEvent !== true){
25279             this.fireEvent('check', this, true);
25280         }
25281
25282         this.validate();
25283         
25284         return;
25285     },
25286     
25287     validate : function()
25288     {
25289         if(this.getVisibilityEl().hasClass('hidden')){
25290             return true;
25291         }
25292         
25293         if(
25294                 this.disabled || 
25295                 (this.inputType == 'radio' && this.validateRadio()) ||
25296                 (this.inputType == 'checkbox' && this.validateCheckbox())
25297         ){
25298             this.markValid();
25299             return true;
25300         }
25301         
25302         this.markInvalid();
25303         return false;
25304     },
25305     
25306     validateRadio : function()
25307     {
25308         if(this.getVisibilityEl().hasClass('hidden')){
25309             return true;
25310         }
25311         
25312         if(this.allowBlank){
25313             return true;
25314         }
25315         
25316         var valid = false;
25317         
25318         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25319             if(!e.dom.checked){
25320                 return;
25321             }
25322             
25323             valid = true;
25324             
25325             return false;
25326         });
25327         
25328         return valid;
25329     },
25330     
25331     validateCheckbox : function()
25332     {
25333         if(!this.groupId){
25334             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25335             //return (this.getValue() == this.inputValue) ? true : false;
25336         }
25337         
25338         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25339         
25340         if(!group){
25341             return false;
25342         }
25343         
25344         var r = false;
25345         
25346         for(var i in group){
25347             if(group[i].el.isVisible(true)){
25348                 r = false;
25349                 break;
25350             }
25351             
25352             r = true;
25353         }
25354         
25355         for(var i in group){
25356             if(r){
25357                 break;
25358             }
25359             
25360             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25361         }
25362         
25363         return r;
25364     },
25365     
25366     /**
25367      * Mark this field as valid
25368      */
25369     markValid : function()
25370     {
25371         var _this = this;
25372         
25373         this.fireEvent('valid', this);
25374         
25375         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25376         
25377         if(this.groupId){
25378             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25379         }
25380         
25381         if(label){
25382             label.markValid();
25383         }
25384
25385         if(this.inputType == 'radio'){
25386             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25387                 var fg = e.findParent('.form-group', false, true);
25388                 if (Roo.bootstrap.version == 3) {
25389                     fg.removeClass([_this.invalidClass, _this.validClass]);
25390                     fg.addClass(_this.validClass);
25391                 } else {
25392                     fg.removeClass(['is-valid', 'is-invalid']);
25393                     fg.addClass('is-valid');
25394                 }
25395             });
25396             
25397             return;
25398         }
25399
25400         if(!this.groupId){
25401             var fg = this.el.findParent('.form-group', false, true);
25402             if (Roo.bootstrap.version == 3) {
25403                 fg.removeClass([this.invalidClass, this.validClass]);
25404                 fg.addClass(this.validClass);
25405             } else {
25406                 fg.removeClass(['is-valid', 'is-invalid']);
25407                 fg.addClass('is-valid');
25408             }
25409             return;
25410         }
25411         
25412         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25413         
25414         if(!group){
25415             return;
25416         }
25417         
25418         for(var i in group){
25419             var fg = group[i].el.findParent('.form-group', false, true);
25420             if (Roo.bootstrap.version == 3) {
25421                 fg.removeClass([this.invalidClass, this.validClass]);
25422                 fg.addClass(this.validClass);
25423             } else {
25424                 fg.removeClass(['is-valid', 'is-invalid']);
25425                 fg.addClass('is-valid');
25426             }
25427         }
25428     },
25429     
25430      /**
25431      * Mark this field as invalid
25432      * @param {String} msg The validation message
25433      */
25434     markInvalid : function(msg)
25435     {
25436         if(this.allowBlank){
25437             return;
25438         }
25439         
25440         var _this = this;
25441         
25442         this.fireEvent('invalid', this, msg);
25443         
25444         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25445         
25446         if(this.groupId){
25447             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25448         }
25449         
25450         if(label){
25451             label.markInvalid();
25452         }
25453             
25454         if(this.inputType == 'radio'){
25455             
25456             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25457                 var fg = e.findParent('.form-group', false, true);
25458                 if (Roo.bootstrap.version == 3) {
25459                     fg.removeClass([_this.invalidClass, _this.validClass]);
25460                     fg.addClass(_this.invalidClass);
25461                 } else {
25462                     fg.removeClass(['is-invalid', 'is-valid']);
25463                     fg.addClass('is-invalid');
25464                 }
25465             });
25466             
25467             return;
25468         }
25469         
25470         if(!this.groupId){
25471             var fg = this.el.findParent('.form-group', false, true);
25472             if (Roo.bootstrap.version == 3) {
25473                 fg.removeClass([_this.invalidClass, _this.validClass]);
25474                 fg.addClass(_this.invalidClass);
25475             } else {
25476                 fg.removeClass(['is-invalid', 'is-valid']);
25477                 fg.addClass('is-invalid');
25478             }
25479             return;
25480         }
25481         
25482         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25483         
25484         if(!group){
25485             return;
25486         }
25487         
25488         for(var i in group){
25489             var fg = group[i].el.findParent('.form-group', false, true);
25490             if (Roo.bootstrap.version == 3) {
25491                 fg.removeClass([_this.invalidClass, _this.validClass]);
25492                 fg.addClass(_this.invalidClass);
25493             } else {
25494                 fg.removeClass(['is-invalid', 'is-valid']);
25495                 fg.addClass('is-invalid');
25496             }
25497         }
25498         
25499     },
25500     
25501     clearInvalid : function()
25502     {
25503         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25504         
25505         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25506         
25507         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25508         
25509         if (label && label.iconEl) {
25510             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25511             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25512         }
25513     },
25514     
25515     disable : function()
25516     {
25517         if(this.inputType != 'radio'){
25518             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25519             return;
25520         }
25521         
25522         var _this = this;
25523         
25524         if(this.rendered){
25525             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25526                 _this.getActionEl().addClass(this.disabledClass);
25527                 e.dom.disabled = true;
25528             });
25529         }
25530         
25531         this.disabled = true;
25532         this.fireEvent("disable", this);
25533         return this;
25534     },
25535
25536     enable : function()
25537     {
25538         if(this.inputType != 'radio'){
25539             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25540             return;
25541         }
25542         
25543         var _this = this;
25544         
25545         if(this.rendered){
25546             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25547                 _this.getActionEl().removeClass(this.disabledClass);
25548                 e.dom.disabled = false;
25549             });
25550         }
25551         
25552         this.disabled = false;
25553         this.fireEvent("enable", this);
25554         return this;
25555     },
25556     
25557     setBoxLabel : function(v)
25558     {
25559         this.boxLabel = v;
25560         
25561         if(this.rendered){
25562             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25563         }
25564     }
25565
25566 });
25567
25568 Roo.apply(Roo.bootstrap.form.CheckBox, {
25569     
25570     groups: {},
25571     
25572      /**
25573     * register a CheckBox Group
25574     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25575     */
25576     register : function(checkbox)
25577     {
25578         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25579             this.groups[checkbox.groupId] = {};
25580         }
25581         
25582         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25583             return;
25584         }
25585         
25586         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25587         
25588     },
25589     /**
25590     * fetch a CheckBox Group based on the group ID
25591     * @param {string} the group ID
25592     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25593     */
25594     get: function(groupId) {
25595         if (typeof(this.groups[groupId]) == 'undefined') {
25596             return false;
25597         }
25598         
25599         return this.groups[groupId] ;
25600     }
25601     
25602     
25603 });
25604 /*
25605  * - LGPL
25606  *
25607  * RadioItem
25608  * 
25609  */
25610
25611 /**
25612  * @class Roo.bootstrap.form.Radio
25613  * @extends Roo.bootstrap.Component
25614  * Bootstrap Radio class
25615  * @cfg {String} boxLabel - the label associated
25616  * @cfg {String} value - the value of radio
25617  * 
25618  * @constructor
25619  * Create a new Radio
25620  * @param {Object} config The config object
25621  */
25622 Roo.bootstrap.form.Radio = function(config){
25623     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25624     
25625 };
25626
25627 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25628     
25629     boxLabel : '',
25630     
25631     value : '',
25632     
25633     getAutoCreate : function()
25634     {
25635         var cfg = {
25636             tag : 'div',
25637             cls : 'form-group radio',
25638             cn : [
25639                 {
25640                     tag : 'label',
25641                     cls : 'box-label',
25642                     html : this.boxLabel
25643                 }
25644             ]
25645         };
25646         
25647         return cfg;
25648     },
25649     
25650     initEvents : function() 
25651     {
25652         this.parent().register(this);
25653         
25654         this.el.on('click', this.onClick, this);
25655         
25656     },
25657     
25658     onClick : function(e)
25659     {
25660         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25661             this.setChecked(true);
25662         }
25663     },
25664     
25665     setChecked : function(state, suppressEvent)
25666     {
25667         this.parent().setValue(this.value, suppressEvent);
25668         
25669     },
25670     
25671     setBoxLabel : function(v)
25672     {
25673         this.boxLabel = v;
25674         
25675         if(this.rendered){
25676             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25677         }
25678     }
25679     
25680 });
25681  
25682
25683  /*
25684  * - LGPL
25685  *
25686  * Input
25687  * 
25688  */
25689
25690 /**
25691  * @class Roo.bootstrap.form.SecurePass
25692  * @extends Roo.bootstrap.form.Input
25693  * Bootstrap SecurePass class
25694  *
25695  * 
25696  * @constructor
25697  * Create a new SecurePass
25698  * @param {Object} config The config object
25699  */
25700  
25701 Roo.bootstrap.form.SecurePass = function (config) {
25702     // these go here, so the translation tool can replace them..
25703     this.errors = {
25704         PwdEmpty: "Please type a password, and then retype it to confirm.",
25705         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25706         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25707         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25708         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25709         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25710         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25711         TooWeak: "Your password is Too Weak."
25712     },
25713     this.meterLabel = "Password strength:";
25714     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25715     this.meterClass = [
25716         "roo-password-meter-tooweak", 
25717         "roo-password-meter-weak", 
25718         "roo-password-meter-medium", 
25719         "roo-password-meter-strong", 
25720         "roo-password-meter-grey"
25721     ];
25722     
25723     this.errors = {};
25724     
25725     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25726 }
25727
25728 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25729     /**
25730      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25731      * {
25732      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25733      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25734      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25735      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25736      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25737      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25738      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25739      * })
25740      */
25741     // private
25742     
25743     meterWidth: 300,
25744     errorMsg :'',    
25745     errors: false,
25746     imageRoot: '/',
25747     /**
25748      * @cfg {String/Object} Label for the strength meter (defaults to
25749      * 'Password strength:')
25750      */
25751     // private
25752     meterLabel: '',
25753     /**
25754      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25755      * ['Weak', 'Medium', 'Strong'])
25756      */
25757     // private    
25758     pwdStrengths: false,    
25759     // private
25760     strength: 0,
25761     // private
25762     _lastPwd: null,
25763     // private
25764     kCapitalLetter: 0,
25765     kSmallLetter: 1,
25766     kDigit: 2,
25767     kPunctuation: 3,
25768     
25769     insecure: false,
25770     // private
25771     initEvents: function ()
25772     {
25773         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25774
25775         if (this.el.is('input[type=password]') && Roo.isSafari) {
25776             this.el.on('keydown', this.SafariOnKeyDown, this);
25777         }
25778
25779         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25780     },
25781     // private
25782     onRender: function (ct, position)
25783     {
25784         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25785         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25786         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25787
25788         this.trigger.createChild({
25789                    cn: [
25790                     {
25791                     //id: 'PwdMeter',
25792                     tag: 'div',
25793                     cls: 'roo-password-meter-grey col-xs-12',
25794                     style: {
25795                         //width: 0,
25796                         //width: this.meterWidth + 'px'                                                
25797                         }
25798                     },
25799                     {                            
25800                          cls: 'roo-password-meter-text'                          
25801                     }
25802                 ]            
25803         });
25804
25805          
25806         if (this.hideTrigger) {
25807             this.trigger.setDisplayed(false);
25808         }
25809         this.setSize(this.width || '', this.height || '');
25810     },
25811     // private
25812     onDestroy: function ()
25813     {
25814         if (this.trigger) {
25815             this.trigger.removeAllListeners();
25816             this.trigger.remove();
25817         }
25818         if (this.wrap) {
25819             this.wrap.remove();
25820         }
25821         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25822     },
25823     // private
25824     checkStrength: function ()
25825     {
25826         var pwd = this.inputEl().getValue();
25827         if (pwd == this._lastPwd) {
25828             return;
25829         }
25830
25831         var strength;
25832         if (this.ClientSideStrongPassword(pwd)) {
25833             strength = 3;
25834         } else if (this.ClientSideMediumPassword(pwd)) {
25835             strength = 2;
25836         } else if (this.ClientSideWeakPassword(pwd)) {
25837             strength = 1;
25838         } else {
25839             strength = 0;
25840         }
25841         
25842         Roo.log('strength1: ' + strength);
25843         
25844         //var pm = this.trigger.child('div/div/div').dom;
25845         var pm = this.trigger.child('div/div');
25846         pm.removeClass(this.meterClass);
25847         pm.addClass(this.meterClass[strength]);
25848                 
25849         
25850         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25851                 
25852         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25853         
25854         this._lastPwd = pwd;
25855     },
25856     reset: function ()
25857     {
25858         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25859         
25860         this._lastPwd = '';
25861         
25862         var pm = this.trigger.child('div/div');
25863         pm.removeClass(this.meterClass);
25864         pm.addClass('roo-password-meter-grey');        
25865         
25866         
25867         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25868         
25869         pt.innerHTML = '';
25870         this.inputEl().dom.type='password';
25871     },
25872     // private
25873     validateValue: function (value)
25874     {
25875         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25876             return false;
25877         }
25878         if (value.length == 0) {
25879             if (this.allowBlank) {
25880                 this.clearInvalid();
25881                 return true;
25882             }
25883
25884             this.markInvalid(this.errors.PwdEmpty);
25885             this.errorMsg = this.errors.PwdEmpty;
25886             return false;
25887         }
25888         
25889         if(this.insecure){
25890             return true;
25891         }
25892         
25893         if (!value.match(/[\x21-\x7e]+/)) {
25894             this.markInvalid(this.errors.PwdBadChar);
25895             this.errorMsg = this.errors.PwdBadChar;
25896             return false;
25897         }
25898         if (value.length < 6) {
25899             this.markInvalid(this.errors.PwdShort);
25900             this.errorMsg = this.errors.PwdShort;
25901             return false;
25902         }
25903         if (value.length > 16) {
25904             this.markInvalid(this.errors.PwdLong);
25905             this.errorMsg = this.errors.PwdLong;
25906             return false;
25907         }
25908         var strength;
25909         if (this.ClientSideStrongPassword(value)) {
25910             strength = 3;
25911         } else if (this.ClientSideMediumPassword(value)) {
25912             strength = 2;
25913         } else if (this.ClientSideWeakPassword(value)) {
25914             strength = 1;
25915         } else {
25916             strength = 0;
25917         }
25918
25919         
25920         if (strength < 2) {
25921             //this.markInvalid(this.errors.TooWeak);
25922             this.errorMsg = this.errors.TooWeak;
25923             //return false;
25924         }
25925         
25926         
25927         console.log('strength2: ' + strength);
25928         
25929         //var pm = this.trigger.child('div/div/div').dom;
25930         
25931         var pm = this.trigger.child('div/div');
25932         pm.removeClass(this.meterClass);
25933         pm.addClass(this.meterClass[strength]);
25934                 
25935         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25936                 
25937         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25938         
25939         this.errorMsg = ''; 
25940         return true;
25941     },
25942     // private
25943     CharacterSetChecks: function (type)
25944     {
25945         this.type = type;
25946         this.fResult = false;
25947     },
25948     // private
25949     isctype: function (character, type)
25950     {
25951         switch (type) {  
25952             case this.kCapitalLetter:
25953                 if (character >= 'A' && character <= 'Z') {
25954                     return true;
25955                 }
25956                 break;
25957             
25958             case this.kSmallLetter:
25959                 if (character >= 'a' && character <= 'z') {
25960                     return true;
25961                 }
25962                 break;
25963             
25964             case this.kDigit:
25965                 if (character >= '0' && character <= '9') {
25966                     return true;
25967                 }
25968                 break;
25969             
25970             case this.kPunctuation:
25971                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25972                     return true;
25973                 }
25974                 break;
25975             
25976             default:
25977                 return false;
25978         }
25979
25980     },
25981     // private
25982     IsLongEnough: function (pwd, size)
25983     {
25984         return !(pwd == null || isNaN(size) || pwd.length < size);
25985     },
25986     // private
25987     SpansEnoughCharacterSets: function (word, nb)
25988     {
25989         if (!this.IsLongEnough(word, nb))
25990         {
25991             return false;
25992         }
25993
25994         var characterSetChecks = new Array(
25995             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25996             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25997         );
25998         
25999         for (var index = 0; index < word.length; ++index) {
26000             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26001                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
26002                     characterSetChecks[nCharSet].fResult = true;
26003                     break;
26004                 }
26005             }
26006         }
26007
26008         var nCharSets = 0;
26009         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26010             if (characterSetChecks[nCharSet].fResult) {
26011                 ++nCharSets;
26012             }
26013         }
26014
26015         if (nCharSets < nb) {
26016             return false;
26017         }
26018         return true;
26019     },
26020     // private
26021     ClientSideStrongPassword: function (pwd)
26022     {
26023         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
26024     },
26025     // private
26026     ClientSideMediumPassword: function (pwd)
26027     {
26028         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
26029     },
26030     // private
26031     ClientSideWeakPassword: function (pwd)
26032     {
26033         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26034     }
26035           
26036 });
26037 Roo.htmleditor = {};
26038  
26039 /**
26040  * @class Roo.htmleditor.Filter
26041  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26042  * @cfg {DomElement} node The node to iterate and filter
26043  * @cfg {boolean|String|Array} tag Tags to replace 
26044  * @constructor
26045  * Create a new Filter.
26046  * @param {Object} config Configuration options
26047  */
26048
26049
26050
26051 Roo.htmleditor.Filter = function(cfg) {
26052     Roo.apply(this.cfg);
26053     // this does not actually call walk as it's really just a abstract class
26054 }
26055
26056
26057 Roo.htmleditor.Filter.prototype = {
26058     
26059     node: false,
26060     
26061     tag: false,
26062
26063     // overrride to do replace comments.
26064     replaceComment : false,
26065     
26066     // overrride to do replace or do stuff with tags..
26067     replaceTag : false,
26068     
26069     walk : function(dom)
26070     {
26071         Roo.each( Array.from(dom.childNodes), function( e ) {
26072             switch(true) {
26073                 
26074                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26075                     this.replaceComment(e);
26076                     return;
26077                 
26078                 case e.nodeType != 1: //not a node.
26079                     return;
26080                 
26081                 case this.tag === true: // everything
26082                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26083                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26084                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26085                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26086                     if (this.replaceTag && false === this.replaceTag(e)) {
26087                         return;
26088                     }
26089                     if (e.hasChildNodes()) {
26090                         this.walk(e);
26091                     }
26092                     return;
26093                 
26094                 default:    // tags .. that do not match.
26095                     if (e.hasChildNodes()) {
26096                         this.walk(e);
26097                     }
26098             }
26099             
26100         }, this);
26101         
26102     },
26103     
26104     
26105     removeNodeKeepChildren : function( node)
26106     {
26107     
26108         ar = Array.from(node.childNodes);
26109         for (var i = 0; i < ar.length; i++) {
26110          
26111             node.removeChild(ar[i]);
26112             // what if we need to walk these???
26113             node.parentNode.insertBefore(ar[i], node);
26114            
26115         }
26116         node.parentNode.removeChild(node);
26117     }
26118 }; 
26119
26120 /**
26121  * @class Roo.htmleditor.FilterAttributes
26122  * clean attributes and  styles including http:// etc.. in attribute
26123  * @constructor
26124 * Run a new Attribute Filter
26125 * @param {Object} config Configuration options
26126  */
26127 Roo.htmleditor.FilterAttributes = function(cfg)
26128 {
26129     Roo.apply(this, cfg);
26130     this.attrib_black = this.attrib_black || [];
26131     this.attrib_white = this.attrib_white || [];
26132
26133     this.attrib_clean = this.attrib_clean || [];
26134     this.style_white = this.style_white || [];
26135     this.style_black = this.style_black || [];
26136     this.walk(cfg.node);
26137 }
26138
26139 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26140 {
26141     tag: true, // all tags
26142     
26143     attrib_black : false, // array
26144     attrib_clean : false,
26145     attrib_white : false,
26146
26147     style_white : false,
26148     style_black : false,
26149      
26150      
26151     replaceTag : function(node)
26152     {
26153         if (!node.attributes || !node.attributes.length) {
26154             return true;
26155         }
26156         
26157         for (var i = node.attributes.length-1; i > -1 ; i--) {
26158             var a = node.attributes[i];
26159             //console.log(a);
26160             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26161                 node.removeAttribute(a.name);
26162                 continue;
26163             }
26164             
26165             
26166             
26167             if (a.name.toLowerCase().substr(0,2)=='on')  {
26168                 node.removeAttribute(a.name);
26169                 continue;
26170             }
26171             
26172             
26173             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26174                 node.removeAttribute(a.name);
26175                 continue;
26176             }
26177             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26178                 this.cleanAttr(node,a.name,a.value); // fixme..
26179                 continue;
26180             }
26181             if (a.name == 'style') {
26182                 this.cleanStyle(node,a.name,a.value);
26183                 continue;
26184             }
26185             /// clean up MS crap..
26186             // tecnically this should be a list of valid class'es..
26187             
26188             
26189             if (a.name == 'class') {
26190                 if (a.value.match(/^Mso/)) {
26191                     node.removeAttribute('class');
26192                 }
26193                 
26194                 if (a.value.match(/^body$/)) {
26195                     node.removeAttribute('class');
26196                 }
26197                 continue;
26198             }
26199             
26200             
26201             // style cleanup!?
26202             // class cleanup?
26203             
26204         }
26205         return true; // clean children
26206     },
26207         
26208     cleanAttr: function(node, n,v)
26209     {
26210         
26211         if (v.match(/^\./) || v.match(/^\//)) {
26212             return;
26213         }
26214         if (v.match(/^(http|https):\/\//)
26215             || v.match(/^mailto:/) 
26216             || v.match(/^ftp:/)
26217             || v.match(/^data:/)
26218             ) {
26219             return;
26220         }
26221         if (v.match(/^#/)) {
26222             return;
26223         }
26224         if (v.match(/^\{/)) { // allow template editing.
26225             return;
26226         }
26227 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26228         node.removeAttribute(n);
26229         
26230     },
26231     cleanStyle : function(node,  n,v)
26232     {
26233         if (v.match(/expression/)) { //XSS?? should we even bother..
26234             node.removeAttribute(n);
26235             return;
26236         }
26237         
26238         var parts = v.split(/;/);
26239         var clean = [];
26240         
26241         Roo.each(parts, function(p) {
26242             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26243             if (!p.length) {
26244                 return true;
26245             }
26246             var l = p.split(':').shift().replace(/\s+/g,'');
26247             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26248             
26249             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26250                 return true;
26251             }
26252             //Roo.log()
26253             // only allow 'c whitelisted system attributes'
26254             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26255                 return true;
26256             }
26257             
26258             
26259             clean.push(p);
26260             return true;
26261         },this);
26262         if (clean.length) { 
26263             node.setAttribute(n, clean.join(';'));
26264         } else {
26265             node.removeAttribute(n);
26266         }
26267         
26268     }
26269         
26270         
26271         
26272     
26273 });/**
26274  * @class Roo.htmleditor.FilterBlack
26275  * remove blacklisted elements.
26276  * @constructor
26277  * Run a new Blacklisted Filter
26278  * @param {Object} config Configuration options
26279  */
26280
26281 Roo.htmleditor.FilterBlack = function(cfg)
26282 {
26283     Roo.apply(this, cfg);
26284     this.walk(cfg.node);
26285 }
26286
26287 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26288 {
26289     tag : true, // all elements.
26290    
26291     replaceTag : function(n)
26292     {
26293         n.parentNode.removeChild(n);
26294     }
26295 });
26296 /**
26297  * @class Roo.htmleditor.FilterComment
26298  * remove comments.
26299  * @constructor
26300 * Run a new Comments Filter
26301 * @param {Object} config Configuration options
26302  */
26303 Roo.htmleditor.FilterComment = function(cfg)
26304 {
26305     this.walk(cfg.node);
26306 }
26307
26308 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26309 {
26310   
26311     replaceComment : function(n)
26312     {
26313         n.parentNode.removeChild(n);
26314     }
26315 });/**
26316  * @class Roo.htmleditor.FilterKeepChildren
26317  * remove tags but keep children
26318  * @constructor
26319  * Run a new Keep Children Filter
26320  * @param {Object} config Configuration options
26321  */
26322
26323 Roo.htmleditor.FilterKeepChildren = function(cfg)
26324 {
26325     Roo.apply(this, cfg);
26326     if (this.tag === false) {
26327         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26328     }
26329     // hacky?
26330     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26331         this.cleanNamespace = true;
26332     }
26333         
26334     this.walk(cfg.node);
26335 }
26336
26337 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26338 {
26339     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26340   
26341     replaceTag : function(node)
26342     {
26343         // walk children...
26344         //Roo.log(node.tagName);
26345         var ar = Array.from(node.childNodes);
26346         //remove first..
26347         
26348         for (var i = 0; i < ar.length; i++) {
26349             var e = ar[i];
26350             if (e.nodeType == 1) {
26351                 if (
26352                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26353                     || // array and it matches
26354                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
26355                     ||
26356                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26357                     ||
26358                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26359                 ) {
26360                     this.replaceTag(ar[i]); // child is blacklisted as well...
26361                     continue;
26362                 }
26363             }
26364         }  
26365         ar = Array.from(node.childNodes);
26366         for (var i = 0; i < ar.length; i++) {
26367          
26368             node.removeChild(ar[i]);
26369             // what if we need to walk these???
26370             node.parentNode.insertBefore(ar[i], node);
26371             if (this.tag !== false) {
26372                 this.walk(ar[i]);
26373                 
26374             }
26375         }
26376         //Roo.log("REMOVE:" + node.tagName);
26377         node.parentNode.removeChild(node);
26378         return false; // don't walk children
26379         
26380         
26381     }
26382 });/**
26383  * @class Roo.htmleditor.FilterParagraph
26384  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26385  * like on 'push' to remove the <p> tags and replace them with line breaks.
26386  * @constructor
26387  * Run a new Paragraph Filter
26388  * @param {Object} config Configuration options
26389  */
26390
26391 Roo.htmleditor.FilterParagraph = function(cfg)
26392 {
26393     // no need to apply config.
26394     this.walk(cfg.node);
26395 }
26396
26397 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26398 {
26399     
26400      
26401     tag : 'P',
26402     
26403      
26404     replaceTag : function(node)
26405     {
26406         
26407         if (node.childNodes.length == 1 &&
26408             node.childNodes[0].nodeType == 3 &&
26409             node.childNodes[0].textContent.trim().length < 1
26410             ) {
26411             // remove and replace with '<BR>';
26412             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26413             return false; // no need to walk..
26414         }
26415         var ar = Array.from(node.childNodes);
26416         for (var i = 0; i < ar.length; i++) {
26417             node.removeChild(ar[i]);
26418             // what if we need to walk these???
26419             node.parentNode.insertBefore(ar[i], node);
26420         }
26421         // now what about this?
26422         // <p> &nbsp; </p>
26423         
26424         // double BR.
26425         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26426         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26427         node.parentNode.removeChild(node);
26428         
26429         return false;
26430
26431     }
26432     
26433 });/**
26434  * @class Roo.htmleditor.FilterSpan
26435  * filter span's with no attributes out..
26436  * @constructor
26437  * Run a new Span Filter
26438  * @param {Object} config Configuration options
26439  */
26440
26441 Roo.htmleditor.FilterSpan = function(cfg)
26442 {
26443     // no need to apply config.
26444     this.walk(cfg.node);
26445 }
26446
26447 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26448 {
26449      
26450     tag : 'SPAN',
26451      
26452  
26453     replaceTag : function(node)
26454     {
26455         if (node.attributes && node.attributes.length > 0) {
26456             return true; // walk if there are any.
26457         }
26458         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26459         return false;
26460      
26461     }
26462     
26463 });/**
26464  * @class Roo.htmleditor.FilterTableWidth
26465   try and remove table width data - as that frequently messes up other stuff.
26466  * 
26467  *      was cleanTableWidths.
26468  *
26469  * Quite often pasting from word etc.. results in tables with column and widths.
26470  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26471  *
26472  * @constructor
26473  * Run a new Table Filter
26474  * @param {Object} config Configuration options
26475  */
26476
26477 Roo.htmleditor.FilterTableWidth = function(cfg)
26478 {
26479     // no need to apply config.
26480     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26481     this.walk(cfg.node);
26482 }
26483
26484 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26485 {
26486      
26487      
26488     
26489     replaceTag: function(node) {
26490         
26491         
26492       
26493         if (node.hasAttribute('width')) {
26494             node.removeAttribute('width');
26495         }
26496         
26497          
26498         if (node.hasAttribute("style")) {
26499             // pretty basic...
26500             
26501             var styles = node.getAttribute("style").split(";");
26502             var nstyle = [];
26503             Roo.each(styles, function(s) {
26504                 if (!s.match(/:/)) {
26505                     return;
26506                 }
26507                 var kv = s.split(":");
26508                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26509                     return;
26510                 }
26511                 // what ever is left... we allow.
26512                 nstyle.push(s);
26513             });
26514             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26515             if (!nstyle.length) {
26516                 node.removeAttribute('style');
26517             }
26518         }
26519         
26520         return true; // continue doing children..
26521     }
26522 });/**
26523  * @class Roo.htmleditor.FilterWord
26524  * try and clean up all the mess that Word generates.
26525  * 
26526  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
26527  
26528  * @constructor
26529  * Run a new Span Filter
26530  * @param {Object} config Configuration options
26531  */
26532
26533 Roo.htmleditor.FilterWord = function(cfg)
26534 {
26535     // no need to apply config.
26536     this.replaceDocBullets(cfg.node);
26537     
26538     this.replaceAname(cfg.node);
26539     // this is disabled as the removal is done by other filters;
26540    // this.walk(cfg.node);
26541     
26542     
26543 }
26544
26545 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
26546 {
26547     tag: true,
26548      
26549     
26550     /**
26551      * Clean up MS wordisms...
26552      */
26553     replaceTag : function(node)
26554     {
26555          
26556         // no idea what this does - span with text, replaceds with just text.
26557         if(
26558                 node.nodeName == 'SPAN' &&
26559                 !node.hasAttributes() &&
26560                 node.childNodes.length == 1 &&
26561                 node.firstChild.nodeName == "#text"  
26562         ) {
26563             var textNode = node.firstChild;
26564             node.removeChild(textNode);
26565             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26566                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26567             }
26568             node.parentNode.insertBefore(textNode, node);
26569             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26570                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26571             }
26572             
26573             node.parentNode.removeChild(node);
26574             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
26575         }
26576         
26577    
26578         
26579         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26580             node.parentNode.removeChild(node);
26581             return false; // dont do chidlren
26582         }
26583         //Roo.log(node.tagName);
26584         // remove - but keep children..
26585         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26586             //Roo.log('-- removed');
26587             while (node.childNodes.length) {
26588                 var cn = node.childNodes[0];
26589                 node.removeChild(cn);
26590                 node.parentNode.insertBefore(cn, node);
26591                 // move node to parent - and clean it..
26592                 if (cn.nodeType == 1) {
26593                     this.replaceTag(cn);
26594                 }
26595                 
26596             }
26597             node.parentNode.removeChild(node);
26598             /// no need to iterate chidlren = it's got none..
26599             //this.iterateChildren(node, this.cleanWord);
26600             return false; // no need to iterate children.
26601         }
26602         // clean styles
26603         if (node.className.length) {
26604             
26605             var cn = node.className.split(/\W+/);
26606             var cna = [];
26607             Roo.each(cn, function(cls) {
26608                 if (cls.match(/Mso[a-zA-Z]+/)) {
26609                     return;
26610                 }
26611                 cna.push(cls);
26612             });
26613             node.className = cna.length ? cna.join(' ') : '';
26614             if (!cna.length) {
26615                 node.removeAttribute("class");
26616             }
26617         }
26618         
26619         if (node.hasAttribute("lang")) {
26620             node.removeAttribute("lang");
26621         }
26622         
26623         if (node.hasAttribute("style")) {
26624             
26625             var styles = node.getAttribute("style").split(";");
26626             var nstyle = [];
26627             Roo.each(styles, function(s) {
26628                 if (!s.match(/:/)) {
26629                     return;
26630                 }
26631                 var kv = s.split(":");
26632                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26633                     return;
26634                 }
26635                 // what ever is left... we allow.
26636                 nstyle.push(s);
26637             });
26638             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26639             if (!nstyle.length) {
26640                 node.removeAttribute('style');
26641             }
26642         }
26643         return true; // do children
26644         
26645         
26646         
26647     },
26648     
26649     styleToObject: function(node)
26650     {
26651         var styles = (node.getAttribute("style") || '').split(";");
26652         var ret = {};
26653         Roo.each(styles, function(s) {
26654             if (!s.match(/:/)) {
26655                 return;
26656             }
26657             var kv = s.split(":");
26658              
26659             // what ever is left... we allow.
26660             ret[kv[0].trim()] = kv[1];
26661         });
26662         return ret;
26663     },
26664     
26665     
26666     replaceAname : function (doc)
26667     {
26668         // replace all the a/name without..
26669         var aa = Array.from(doc.getElementsByTagName('a'));
26670         for (var i = 0; i  < aa.length; i++) {
26671             var a = aa[i];
26672             if (a.hasAttribute("name")) {
26673                 a.removeAttribute("name");
26674             }
26675             if (a.hasAttribute("href")) {
26676                 continue;
26677             }
26678             // reparent children.
26679             this.removeNodeKeepChildren(a);
26680             
26681         }
26682         
26683         
26684         
26685     },
26686
26687     
26688     
26689     replaceDocBullets : function(doc)
26690     {
26691         // this is a bit odd - but it appears some indents use ql-indent-1
26692          //Roo.log(doc.innerHTML);
26693         
26694         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
26695         for( var i = 0; i < listpara.length; i ++) {
26696             listpara[i].className = "MsoListParagraph";
26697         }
26698         
26699         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
26700         for( var i = 0; i < listpara.length; i ++) {
26701             listpara[i].className = "MsoListParagraph";
26702         }
26703         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
26704         for( var i = 0; i < listpara.length; i ++) {
26705             listpara[i].className = "MsoListParagraph";
26706         }
26707         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
26708         for( var i = 0; i < listpara.length; i ++) {
26709             listpara[i].className = "MsoListParagraph";
26710         }
26711         
26712         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
26713         var htwo =  Array.from(doc.getElementsByTagName('h2'));
26714         for( var i = 0; i < htwo.length; i ++) {
26715             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
26716                 htwo[i].className = "MsoListParagraph";
26717             }
26718         }
26719         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
26720         for( var i = 0; i < listpara.length; i ++) {
26721             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
26722                 listpara[i].className = "MsoListParagraph";
26723             } else {
26724                 listpara[i].className = "MsoNormalx";
26725             }
26726         }
26727        
26728         listpara = doc.getElementsByClassName('MsoListParagraph');
26729         // Roo.log(doc.innerHTML);
26730         
26731         
26732         
26733         while(listpara.length) {
26734             
26735             this.replaceDocBullet(listpara.item(0));
26736         }
26737       
26738     },
26739     
26740      
26741     
26742     replaceDocBullet : function(p)
26743     {
26744         // gather all the siblings.
26745         var ns = p,
26746             parent = p.parentNode,
26747             doc = parent.ownerDocument,
26748             items = [];
26749             
26750         var listtype = 'ul';   
26751         while (ns) {
26752             if (ns.nodeType != 1) {
26753                 ns = ns.nextSibling;
26754                 continue;
26755             }
26756             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
26757                 break;
26758             }
26759             var spans = ns.getElementsByTagName('span');
26760             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
26761                 items.push(ns);
26762                 ns = ns.nextSibling;
26763                 has_list = true;
26764                 if (spans.length && spans[0].hasAttribute('style')) {
26765                     var  style = this.styleToObject(spans[0]);
26766                     if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
26767                         listtype = 'ol';
26768                     }
26769                 }
26770                 
26771                 continue;
26772             }
26773             var spans = ns.getElementsByTagName('span');
26774             if (!spans.length) {
26775                 break;
26776             }
26777             var has_list  = false;
26778             for(var i = 0; i < spans.length; i++) {
26779                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
26780                     has_list = true;
26781                     break;
26782                 }
26783             }
26784             if (!has_list) {
26785                 break;
26786             }
26787             items.push(ns);
26788             ns = ns.nextSibling;
26789             
26790             
26791         }
26792         if (!items.length) {
26793             ns.className = "";
26794             return;
26795         }
26796         
26797         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
26798         parent.insertBefore(ul, p);
26799         var lvl = 0;
26800         var stack = [ ul ];
26801         var last_li = false;
26802         
26803         var margin_to_depth = {};
26804         max_margins = -1;
26805         
26806         items.forEach(function(n, ipos) {
26807             //Roo.log("got innertHMLT=" + n.innerHTML);
26808             
26809             var spans = n.getElementsByTagName('span');
26810             if (!spans.length) {
26811                 //Roo.log("No spans found");
26812                  
26813                 parent.removeChild(n);
26814                 
26815                 
26816                 return; // skip it...
26817             }
26818            
26819                 
26820             var num = 1;
26821             var style = {};
26822             for(var i = 0; i < spans.length; i++) {
26823             
26824                 style = this.styleToObject(spans[i]);
26825                 if (typeof(style['mso-list']) == 'undefined') {
26826                     continue;
26827                 }
26828                 if (listtype == 'ol') {
26829                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
26830                 }
26831                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
26832                 break;
26833             }
26834             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
26835             style = this.styleToObject(n); // mo-list is from the parent node.
26836             if (typeof(style['mso-list']) == 'undefined') {
26837                 //Roo.log("parent is missing level");
26838                   
26839                 parent.removeChild(n);
26840                  
26841                 return;
26842             }
26843             
26844             var margin = style['margin-left'];
26845             if (typeof(margin_to_depth[margin]) == 'undefined') {
26846                 max_margins++;
26847                 margin_to_depth[margin] = max_margins;
26848             }
26849             nlvl = margin_to_depth[margin] ;
26850              
26851             if (nlvl > lvl) {
26852                 //new indent
26853                 var nul = doc.createElement(listtype); // what about number lists...
26854                 if (!last_li) {
26855                     last_li = doc.createElement('li');
26856                     stack[lvl].appendChild(last_li);
26857                 }
26858                 last_li.appendChild(nul);
26859                 stack[nlvl] = nul;
26860                 
26861             }
26862             lvl = nlvl;
26863             
26864             // not starting at 1..
26865             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
26866                 stack[nlvl].setAttribute("start", num);
26867             }
26868             
26869             var nli = stack[nlvl].appendChild(doc.createElement('li'));
26870             last_li = nli;
26871             nli.innerHTML = n.innerHTML;
26872             //Roo.log("innerHTML = " + n.innerHTML);
26873             parent.removeChild(n);
26874             
26875              
26876              
26877             
26878         },this);
26879         
26880         
26881         
26882         
26883     }
26884     
26885     
26886     
26887 });
26888 /**
26889  * @class Roo.htmleditor.FilterStyleToTag
26890  * part of the word stuff... - certain 'styles' should be converted to tags.
26891  * eg.
26892  *   font-weight: bold -> bold
26893  *   ?? super / subscrit etc..
26894  * 
26895  * @constructor
26896 * Run a new style to tag filter.
26897 * @param {Object} config Configuration options
26898  */
26899 Roo.htmleditor.FilterStyleToTag = function(cfg)
26900 {
26901     
26902     this.tags = {
26903         B  : [ 'fontWeight' , 'bold'],
26904         I :  [ 'fontStyle' , 'italic'],
26905         //pre :  [ 'font-style' , 'italic'],
26906         // h1.. h6 ?? font-size?
26907         SUP : [ 'verticalAlign' , 'super' ],
26908         SUB : [ 'verticalAlign' , 'sub' ]
26909         
26910         
26911     };
26912     
26913     Roo.apply(this, cfg);
26914      
26915     
26916     this.walk(cfg.node);
26917     
26918     
26919     
26920 }
26921
26922
26923 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
26924 {
26925     tag: true, // all tags
26926     
26927     tags : false,
26928     
26929     
26930     replaceTag : function(node)
26931     {
26932         
26933         
26934         if (node.getAttribute("style") === null) {
26935             return true;
26936         }
26937         var inject = [];
26938         for (var k in this.tags) {
26939             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
26940                 inject.push(k);
26941                 node.style.removeProperty(this.tags[k][0]);
26942             }
26943         }
26944         if (!inject.length) {
26945             return true; 
26946         }
26947         var cn = Array.from(node.childNodes);
26948         var nn = node;
26949         Roo.each(inject, function(t) {
26950             var nc = node.ownerDocument.createElement(t);
26951             nn.appendChild(nc);
26952             nn = nc;
26953         });
26954         for(var i = 0;i < cn.length;cn++) {
26955             node.removeChild(cn[i]);
26956             nn.appendChild(cn[i]);
26957         }
26958         return true /// iterate thru
26959     }
26960     
26961 })/**
26962  * @class Roo.htmleditor.FilterLongBr
26963  * BR/BR/BR - keep a maximum of 2...
26964  * @constructor
26965  * Run a new Long BR Filter
26966  * @param {Object} config Configuration options
26967  */
26968
26969 Roo.htmleditor.FilterLongBr = function(cfg)
26970 {
26971     // no need to apply config.
26972     this.walk(cfg.node);
26973 }
26974
26975 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
26976 {
26977     
26978      
26979     tag : 'BR',
26980     
26981      
26982     replaceTag : function(node)
26983     {
26984         
26985         var ps = node.nextSibling;
26986         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26987             ps = ps.nextSibling;
26988         }
26989         
26990         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
26991             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
26992             return false;
26993         }
26994         
26995         if (!ps || ps.nodeType != 1) {
26996             return false;
26997         }
26998         
26999         if (!ps || ps.tagName != 'BR') {
27000            
27001             return false;
27002         }
27003         
27004         
27005         
27006         
27007         
27008         if (!node.previousSibling) {
27009             return false;
27010         }
27011         var ps = node.previousSibling;
27012         
27013         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27014             ps = ps.previousSibling;
27015         }
27016         if (!ps || ps.nodeType != 1) {
27017             return false;
27018         }
27019         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27020         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27021             return false;
27022         }
27023         
27024         node.parentNode.removeChild(node); // remove me...
27025         
27026         return false; // no need to do children
27027
27028     }
27029     
27030 }); 
27031
27032 /**
27033  * @class Roo.htmleditor.FilterBlock
27034  * removes id / data-block and contenteditable that are associated with blocks
27035  * usage should be done on a cloned copy of the dom
27036  * @constructor
27037 * Run a new Attribute Filter { node : xxxx }}
27038 * @param {Object} config Configuration options
27039  */
27040 Roo.htmleditor.FilterBlock = function(cfg)
27041 {
27042     Roo.apply(this, cfg);
27043     var qa = cfg.node.querySelectorAll;
27044     this.removeAttributes('data-block');
27045     this.removeAttributes('contenteditable');
27046     this.removeAttributes('id');
27047     
27048 }
27049
27050 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27051 {
27052     node: true, // all tags
27053      
27054      
27055     removeAttributes : function(attr)
27056     {
27057         var ar = this.node.querySelectorAll('*[' + attr + ']');
27058         for (var i =0;i<ar.length;i++) {
27059             ar[i].removeAttribute(attr);
27060         }
27061     }
27062         
27063         
27064         
27065     
27066 });
27067 /***
27068  * This is based loosely on tinymce 
27069  * @class Roo.htmleditor.TidySerializer
27070  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27071  * @constructor
27072  * @method Serializer
27073  * @param {Object} settings Name/value settings object.
27074  */
27075
27076
27077 Roo.htmleditor.TidySerializer = function(settings)
27078 {
27079     Roo.apply(this, settings);
27080     
27081     this.writer = new Roo.htmleditor.TidyWriter(settings);
27082     
27083     
27084
27085 };
27086 Roo.htmleditor.TidySerializer.prototype = {
27087     
27088     /**
27089      * @param {boolean} inner do the inner of the node.
27090      */
27091     inner : false,
27092     
27093     writer : false,
27094     
27095     /**
27096     * Serializes the specified node into a string.
27097     *
27098     * @example
27099     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27100     * @method serialize
27101     * @param {DomElement} node Node instance to serialize.
27102     * @return {String} String with HTML based on DOM tree.
27103     */
27104     serialize : function(node) {
27105         
27106         // = settings.validate;
27107         var writer = this.writer;
27108         var self  = this;
27109         this.handlers = {
27110             // #text
27111             3: function(node) {
27112                 
27113                 writer.text(node.nodeValue, node);
27114             },
27115             // #comment
27116             8: function(node) {
27117                 writer.comment(node.nodeValue);
27118             },
27119             // Processing instruction
27120             7: function(node) {
27121                 writer.pi(node.name, node.nodeValue);
27122             },
27123             // Doctype
27124             10: function(node) {
27125                 writer.doctype(node.nodeValue);
27126             },
27127             // CDATA
27128             4: function(node) {
27129                 writer.cdata(node.nodeValue);
27130             },
27131             // Document fragment
27132             11: function(node) {
27133                 node = node.firstChild;
27134                 if (!node) {
27135                     return;
27136                 }
27137                 while(node) {
27138                     self.walk(node);
27139                     node = node.nextSibling
27140                 }
27141             }
27142         };
27143         writer.reset();
27144         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27145         return writer.getContent();
27146     },
27147
27148     walk: function(node)
27149     {
27150         var attrName, attrValue, sortedAttrs, i, l, elementRule,
27151             handler = this.handlers[node.nodeType];
27152             
27153         if (handler) {
27154             handler(node);
27155             return;
27156         }
27157     
27158         var name = node.nodeName;
27159         var isEmpty = node.childNodes.length < 1;
27160       
27161         var writer = this.writer;
27162         var attrs = node.attributes;
27163         // Sort attributes
27164         
27165         writer.start(node.nodeName, attrs, isEmpty, node);
27166         if (isEmpty) {
27167             return;
27168         }
27169         node = node.firstChild;
27170         if (!node) {
27171             writer.end(name);
27172             return;
27173         }
27174         while (node) {
27175             this.walk(node);
27176             node = node.nextSibling;
27177         }
27178         writer.end(name);
27179         
27180     
27181     }
27182     // Serialize element and treat all non elements as fragments
27183    
27184 }; 
27185
27186 /***
27187  * This is based loosely on tinymce 
27188  * @class Roo.htmleditor.TidyWriter
27189  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27190  *
27191  * Known issues?
27192  * - not tested much with 'PRE' formated elements.
27193  * 
27194  *
27195  *
27196  */
27197
27198 Roo.htmleditor.TidyWriter = function(settings)
27199 {
27200     
27201     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27202     Roo.apply(this, settings);
27203     this.html = [];
27204     this.state = [];
27205      
27206     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27207   
27208 }
27209 Roo.htmleditor.TidyWriter.prototype = {
27210
27211  
27212     state : false,
27213     
27214     indent :  '  ',
27215     
27216     // part of state...
27217     indentstr : '',
27218     in_pre: false,
27219     in_inline : false,
27220     last_inline : false,
27221     encode : false,
27222      
27223     
27224             /**
27225     * Writes the a start element such as <p id="a">.
27226     *
27227     * @method start
27228     * @param {String} name Name of the element.
27229     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27230     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27231     */
27232     start: function(name, attrs, empty, node)
27233     {
27234         var i, l, attr, value;
27235         
27236         // there are some situations where adding line break && indentation will not work. will not work.
27237         // <span / b / i ... formating?
27238         
27239         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27240         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27241         
27242         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27243         
27244         var add_lb = name == 'BR' ? false : in_inline;
27245         
27246         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27247             i_inline = false;
27248         }
27249
27250         var indentstr =  this.indentstr;
27251         
27252         // e_inline = elements that can be inline, but still allow \n before and after?
27253         // only 'BR' ??? any others?
27254         
27255         // ADD LINE BEFORE tage
27256         if (!this.in_pre) {
27257             if (in_inline) {
27258                 //code
27259                 if (name == 'BR') {
27260                     this.addLine();
27261                 } else if (this.lastElementEndsWS()) {
27262                     this.addLine();
27263                 } else{
27264                     // otherwise - no new line. (and dont indent.)
27265                     indentstr = '';
27266                 }
27267                 
27268             } else {
27269                 this.addLine();
27270             }
27271         } else {
27272             indentstr = '';
27273         }
27274         
27275         this.html.push(indentstr + '<', name.toLowerCase());
27276         
27277         if (attrs) {
27278             for (i = 0, l = attrs.length; i < l; i++) {
27279                 attr = attrs[i];
27280                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27281             }
27282         }
27283      
27284         if (empty) {
27285             if (is_short) {
27286                 this.html[this.html.length] = '/>';
27287             } else {
27288                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27289             }
27290             var e_inline = name == 'BR' ? false : this.in_inline;
27291             
27292             if (!e_inline && !this.in_pre) {
27293                 this.addLine();
27294             }
27295             return;
27296         
27297         }
27298         // not empty..
27299         this.html[this.html.length] = '>';
27300         
27301         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27302         /*
27303         if (!in_inline && !in_pre) {
27304             var cn = node.firstChild;
27305             while(cn) {
27306                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27307                     in_inline = true
27308                     break;
27309                 }
27310                 cn = cn.nextSibling;
27311             }
27312              
27313         }
27314         */
27315         
27316         
27317         this.pushState({
27318             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
27319             in_pre : in_pre,
27320             in_inline :  in_inline
27321         });
27322         // add a line after if we are not in a
27323         
27324         if (!in_inline && !in_pre) {
27325             this.addLine();
27326         }
27327         
27328             
27329          
27330         
27331     },
27332     
27333     lastElementEndsWS : function()
27334     {
27335         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27336         if (value === false) {
27337             return true;
27338         }
27339         return value.match(/\s+$/);
27340         
27341     },
27342     
27343     /**
27344      * Writes the a end element such as </p>.
27345      *
27346      * @method end
27347      * @param {String} name Name of the element.
27348      */
27349     end: function(name) {
27350         var value;
27351         this.popState();
27352         var indentstr = '';
27353         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27354         
27355         if (!this.in_pre && !in_inline) {
27356             this.addLine();
27357             indentstr  = this.indentstr;
27358         }
27359         this.html.push(indentstr + '</', name.toLowerCase(), '>');
27360         this.last_inline = in_inline;
27361         
27362         // pop the indent state..
27363     },
27364     /**
27365      * Writes a text node.
27366      *
27367      * In pre - we should not mess with the contents.
27368      * 
27369      *
27370      * @method text
27371      * @param {String} text String to write out.
27372      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27373      */
27374     text: function(in_text, node)
27375     {
27376         // if not in whitespace critical
27377         if (in_text.length < 1) {
27378             return;
27379         }
27380         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27381         
27382         if (this.in_pre) {
27383             this.html[this.html.length] =  text;
27384             return;   
27385         }
27386         
27387         if (this.in_inline) {
27388             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27389             if (text != ' ') {
27390                 text = text.replace(/\s+/,' ');  // all white space to single white space
27391                 
27392                     
27393                 // if next tag is '<BR>', then we can trim right..
27394                 if (node.nextSibling &&
27395                     node.nextSibling.nodeType == 1 &&
27396                     node.nextSibling.nodeName == 'BR' )
27397                 {
27398                     text = text.replace(/\s+$/g,'');
27399                 }
27400                 // if previous tag was a BR, we can also trim..
27401                 if (node.previousSibling &&
27402                     node.previousSibling.nodeType == 1 &&
27403                     node.previousSibling.nodeName == 'BR' )
27404                 {
27405                     text = this.indentstr +  text.replace(/^\s+/g,'');
27406                 }
27407                 if (text.match(/\n/)) {
27408                     text = text.replace(
27409                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27410                     );
27411                     // remoeve the last whitespace / line break.
27412                     text = text.replace(/\n\s+$/,'');
27413                 }
27414                 // repace long lines
27415                 
27416             }
27417              
27418             this.html[this.html.length] =  text;
27419             return;   
27420         }
27421         // see if previous element was a inline element.
27422         var indentstr = this.indentstr;
27423    
27424         text = text.replace(/\s+/g," "); // all whitespace into single white space.
27425         
27426         // should trim left?
27427         if (node.previousSibling &&
27428             node.previousSibling.nodeType == 1 &&
27429             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27430         {
27431             indentstr = '';
27432             
27433         } else {
27434             this.addLine();
27435             text = text.replace(/^\s+/,''); // trim left
27436           
27437         }
27438         // should trim right?
27439         if (node.nextSibling &&
27440             node.nextSibling.nodeType == 1 &&
27441             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27442         {
27443           // noop
27444             
27445         }  else {
27446             text = text.replace(/\s+$/,''); // trim right
27447         }
27448          
27449               
27450         
27451         
27452         
27453         if (text.length < 1) {
27454             return;
27455         }
27456         if (!text.match(/\n/)) {
27457             this.html.push(indentstr + text);
27458             return;
27459         }
27460         
27461         text = this.indentstr + text.replace(
27462             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27463         );
27464         // remoeve the last whitespace / line break.
27465         text = text.replace(/\s+$/,''); 
27466         
27467         this.html.push(text);
27468         
27469         // split and indent..
27470         
27471         
27472     },
27473     /**
27474      * Writes a cdata node such as <![CDATA[data]]>.
27475      *
27476      * @method cdata
27477      * @param {String} text String to write out inside the cdata.
27478      */
27479     cdata: function(text) {
27480         this.html.push('<![CDATA[', text, ']]>');
27481     },
27482     /**
27483     * Writes a comment node such as <!-- Comment -->.
27484     *
27485     * @method cdata
27486     * @param {String} text String to write out inside the comment.
27487     */
27488    comment: function(text) {
27489        this.html.push('<!--', text, '-->');
27490    },
27491     /**
27492      * Writes a PI node such as <?xml attr="value" ?>.
27493      *
27494      * @method pi
27495      * @param {String} name Name of the pi.
27496      * @param {String} text String to write out inside the pi.
27497      */
27498     pi: function(name, text) {
27499         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
27500         this.indent != '' && this.html.push('\n');
27501     },
27502     /**
27503      * Writes a doctype node such as <!DOCTYPE data>.
27504      *
27505      * @method doctype
27506      * @param {String} text String to write out inside the doctype.
27507      */
27508     doctype: function(text) {
27509         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
27510     },
27511     /**
27512      * Resets the internal buffer if one wants to reuse the writer.
27513      *
27514      * @method reset
27515      */
27516     reset: function() {
27517         this.html.length = 0;
27518         this.state = [];
27519         this.pushState({
27520             indentstr : '',
27521             in_pre : false, 
27522             in_inline : false
27523         })
27524     },
27525     /**
27526      * Returns the contents that got serialized.
27527      *
27528      * @method getContent
27529      * @return {String} HTML contents that got written down.
27530      */
27531     getContent: function() {
27532         return this.html.join('').replace(/\n$/, '');
27533     },
27534     
27535     pushState : function(cfg)
27536     {
27537         this.state.push(cfg);
27538         Roo.apply(this, cfg);
27539     },
27540     
27541     popState : function()
27542     {
27543         if (this.state.length < 1) {
27544             return; // nothing to push
27545         }
27546         var cfg = {
27547             in_pre: false,
27548             indentstr : ''
27549         };
27550         this.state.pop();
27551         if (this.state.length > 0) {
27552             cfg = this.state[this.state.length-1]; 
27553         }
27554         Roo.apply(this, cfg);
27555     },
27556     
27557     addLine: function()
27558     {
27559         if (this.html.length < 1) {
27560             return;
27561         }
27562         
27563         
27564         var value = this.html[this.html.length - 1];
27565         if (value.length > 0 && '\n' !== value) {
27566             this.html.push('\n');
27567         }
27568     }
27569     
27570     
27571 //'pre script noscript style textarea video audio iframe object code'
27572 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
27573 // inline 
27574 };
27575
27576 Roo.htmleditor.TidyWriter.inline_elements = [
27577         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
27578         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
27579 ];
27580 Roo.htmleditor.TidyWriter.shortend_elements = [
27581     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
27582     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
27583 ];
27584
27585 Roo.htmleditor.TidyWriter.whitespace_elements = [
27586     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
27587 ];/***
27588  * This is based loosely on tinymce 
27589  * @class Roo.htmleditor.TidyEntities
27590  * @static
27591  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27592  *
27593  * Not 100% sure this is actually used or needed.
27594  */
27595
27596 Roo.htmleditor.TidyEntities = {
27597     
27598     /**
27599      * initialize data..
27600      */
27601     init : function (){
27602      
27603         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
27604        
27605     },
27606
27607
27608     buildEntitiesLookup: function(items, radix) {
27609         var i, chr, entity, lookup = {};
27610         if (!items) {
27611             return {};
27612         }
27613         items = typeof(items) == 'string' ? items.split(',') : items;
27614         radix = radix || 10;
27615         // Build entities lookup table
27616         for (i = 0; i < items.length; i += 2) {
27617             chr = String.fromCharCode(parseInt(items[i], radix));
27618             // Only add non base entities
27619             if (!this.baseEntities[chr]) {
27620                 entity = '&' + items[i + 1] + ';';
27621                 lookup[chr] = entity;
27622                 lookup[entity] = chr;
27623             }
27624         }
27625         return lookup;
27626         
27627     },
27628     
27629     asciiMap : {
27630             128: '€',
27631             130: '‚',
27632             131: 'ƒ',
27633             132: '„',
27634             133: '…',
27635             134: '†',
27636             135: '‡',
27637             136: 'ˆ',
27638             137: '‰',
27639             138: 'Š',
27640             139: '‹',
27641             140: 'Œ',
27642             142: 'Ž',
27643             145: '‘',
27644             146: '’',
27645             147: '“',
27646             148: '”',
27647             149: '•',
27648             150: '–',
27649             151: '—',
27650             152: '˜',
27651             153: '™',
27652             154: 'š',
27653             155: '›',
27654             156: 'œ',
27655             158: 'ž',
27656             159: 'Ÿ'
27657     },
27658     // Raw entities
27659     baseEntities : {
27660         '"': '&quot;',
27661         // Needs to be escaped since the YUI compressor would otherwise break the code
27662         '\'': '&#39;',
27663         '<': '&lt;',
27664         '>': '&gt;',
27665         '&': '&amp;',
27666         '`': '&#96;'
27667     },
27668     // Reverse lookup table for raw entities
27669     reverseEntities : {
27670         '&lt;': '<',
27671         '&gt;': '>',
27672         '&amp;': '&',
27673         '&quot;': '"',
27674         '&apos;': '\''
27675     },
27676     
27677     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
27678     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
27679     rawCharsRegExp : /[<>&\"\']/g,
27680     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
27681     namedEntities  : false,
27682     namedEntitiesData : [ 
27683         '50',
27684         'nbsp',
27685         '51',
27686         'iexcl',
27687         '52',
27688         'cent',
27689         '53',
27690         'pound',
27691         '54',
27692         'curren',
27693         '55',
27694         'yen',
27695         '56',
27696         'brvbar',
27697         '57',
27698         'sect',
27699         '58',
27700         'uml',
27701         '59',
27702         'copy',
27703         '5a',
27704         'ordf',
27705         '5b',
27706         'laquo',
27707         '5c',
27708         'not',
27709         '5d',
27710         'shy',
27711         '5e',
27712         'reg',
27713         '5f',
27714         'macr',
27715         '5g',
27716         'deg',
27717         '5h',
27718         'plusmn',
27719         '5i',
27720         'sup2',
27721         '5j',
27722         'sup3',
27723         '5k',
27724         'acute',
27725         '5l',
27726         'micro',
27727         '5m',
27728         'para',
27729         '5n',
27730         'middot',
27731         '5o',
27732         'cedil',
27733         '5p',
27734         'sup1',
27735         '5q',
27736         'ordm',
27737         '5r',
27738         'raquo',
27739         '5s',
27740         'frac14',
27741         '5t',
27742         'frac12',
27743         '5u',
27744         'frac34',
27745         '5v',
27746         'iquest',
27747         '60',
27748         'Agrave',
27749         '61',
27750         'Aacute',
27751         '62',
27752         'Acirc',
27753         '63',
27754         'Atilde',
27755         '64',
27756         'Auml',
27757         '65',
27758         'Aring',
27759         '66',
27760         'AElig',
27761         '67',
27762         'Ccedil',
27763         '68',
27764         'Egrave',
27765         '69',
27766         'Eacute',
27767         '6a',
27768         'Ecirc',
27769         '6b',
27770         'Euml',
27771         '6c',
27772         'Igrave',
27773         '6d',
27774         'Iacute',
27775         '6e',
27776         'Icirc',
27777         '6f',
27778         'Iuml',
27779         '6g',
27780         'ETH',
27781         '6h',
27782         'Ntilde',
27783         '6i',
27784         'Ograve',
27785         '6j',
27786         'Oacute',
27787         '6k',
27788         'Ocirc',
27789         '6l',
27790         'Otilde',
27791         '6m',
27792         'Ouml',
27793         '6n',
27794         'times',
27795         '6o',
27796         'Oslash',
27797         '6p',
27798         'Ugrave',
27799         '6q',
27800         'Uacute',
27801         '6r',
27802         'Ucirc',
27803         '6s',
27804         'Uuml',
27805         '6t',
27806         'Yacute',
27807         '6u',
27808         'THORN',
27809         '6v',
27810         'szlig',
27811         '70',
27812         'agrave',
27813         '71',
27814         'aacute',
27815         '72',
27816         'acirc',
27817         '73',
27818         'atilde',
27819         '74',
27820         'auml',
27821         '75',
27822         'aring',
27823         '76',
27824         'aelig',
27825         '77',
27826         'ccedil',
27827         '78',
27828         'egrave',
27829         '79',
27830         'eacute',
27831         '7a',
27832         'ecirc',
27833         '7b',
27834         'euml',
27835         '7c',
27836         'igrave',
27837         '7d',
27838         'iacute',
27839         '7e',
27840         'icirc',
27841         '7f',
27842         'iuml',
27843         '7g',
27844         'eth',
27845         '7h',
27846         'ntilde',
27847         '7i',
27848         'ograve',
27849         '7j',
27850         'oacute',
27851         '7k',
27852         'ocirc',
27853         '7l',
27854         'otilde',
27855         '7m',
27856         'ouml',
27857         '7n',
27858         'divide',
27859         '7o',
27860         'oslash',
27861         '7p',
27862         'ugrave',
27863         '7q',
27864         'uacute',
27865         '7r',
27866         'ucirc',
27867         '7s',
27868         'uuml',
27869         '7t',
27870         'yacute',
27871         '7u',
27872         'thorn',
27873         '7v',
27874         'yuml',
27875         'ci',
27876         'fnof',
27877         'sh',
27878         'Alpha',
27879         'si',
27880         'Beta',
27881         'sj',
27882         'Gamma',
27883         'sk',
27884         'Delta',
27885         'sl',
27886         'Epsilon',
27887         'sm',
27888         'Zeta',
27889         'sn',
27890         'Eta',
27891         'so',
27892         'Theta',
27893         'sp',
27894         'Iota',
27895         'sq',
27896         'Kappa',
27897         'sr',
27898         'Lambda',
27899         'ss',
27900         'Mu',
27901         'st',
27902         'Nu',
27903         'su',
27904         'Xi',
27905         'sv',
27906         'Omicron',
27907         't0',
27908         'Pi',
27909         't1',
27910         'Rho',
27911         't3',
27912         'Sigma',
27913         't4',
27914         'Tau',
27915         't5',
27916         'Upsilon',
27917         't6',
27918         'Phi',
27919         't7',
27920         'Chi',
27921         't8',
27922         'Psi',
27923         't9',
27924         'Omega',
27925         'th',
27926         'alpha',
27927         'ti',
27928         'beta',
27929         'tj',
27930         'gamma',
27931         'tk',
27932         'delta',
27933         'tl',
27934         'epsilon',
27935         'tm',
27936         'zeta',
27937         'tn',
27938         'eta',
27939         'to',
27940         'theta',
27941         'tp',
27942         'iota',
27943         'tq',
27944         'kappa',
27945         'tr',
27946         'lambda',
27947         'ts',
27948         'mu',
27949         'tt',
27950         'nu',
27951         'tu',
27952         'xi',
27953         'tv',
27954         'omicron',
27955         'u0',
27956         'pi',
27957         'u1',
27958         'rho',
27959         'u2',
27960         'sigmaf',
27961         'u3',
27962         'sigma',
27963         'u4',
27964         'tau',
27965         'u5',
27966         'upsilon',
27967         'u6',
27968         'phi',
27969         'u7',
27970         'chi',
27971         'u8',
27972         'psi',
27973         'u9',
27974         'omega',
27975         'uh',
27976         'thetasym',
27977         'ui',
27978         'upsih',
27979         'um',
27980         'piv',
27981         '812',
27982         'bull',
27983         '816',
27984         'hellip',
27985         '81i',
27986         'prime',
27987         '81j',
27988         'Prime',
27989         '81u',
27990         'oline',
27991         '824',
27992         'frasl',
27993         '88o',
27994         'weierp',
27995         '88h',
27996         'image',
27997         '88s',
27998         'real',
27999         '892',
28000         'trade',
28001         '89l',
28002         'alefsym',
28003         '8cg',
28004         'larr',
28005         '8ch',
28006         'uarr',
28007         '8ci',
28008         'rarr',
28009         '8cj',
28010         'darr',
28011         '8ck',
28012         'harr',
28013         '8dl',
28014         'crarr',
28015         '8eg',
28016         'lArr',
28017         '8eh',
28018         'uArr',
28019         '8ei',
28020         'rArr',
28021         '8ej',
28022         'dArr',
28023         '8ek',
28024         'hArr',
28025         '8g0',
28026         'forall',
28027         '8g2',
28028         'part',
28029         '8g3',
28030         'exist',
28031         '8g5',
28032         'empty',
28033         '8g7',
28034         'nabla',
28035         '8g8',
28036         'isin',
28037         '8g9',
28038         'notin',
28039         '8gb',
28040         'ni',
28041         '8gf',
28042         'prod',
28043         '8gh',
28044         'sum',
28045         '8gi',
28046         'minus',
28047         '8gn',
28048         'lowast',
28049         '8gq',
28050         'radic',
28051         '8gt',
28052         'prop',
28053         '8gu',
28054         'infin',
28055         '8h0',
28056         'ang',
28057         '8h7',
28058         'and',
28059         '8h8',
28060         'or',
28061         '8h9',
28062         'cap',
28063         '8ha',
28064         'cup',
28065         '8hb',
28066         'int',
28067         '8hk',
28068         'there4',
28069         '8hs',
28070         'sim',
28071         '8i5',
28072         'cong',
28073         '8i8',
28074         'asymp',
28075         '8j0',
28076         'ne',
28077         '8j1',
28078         'equiv',
28079         '8j4',
28080         'le',
28081         '8j5',
28082         'ge',
28083         '8k2',
28084         'sub',
28085         '8k3',
28086         'sup',
28087         '8k4',
28088         'nsub',
28089         '8k6',
28090         'sube',
28091         '8k7',
28092         'supe',
28093         '8kl',
28094         'oplus',
28095         '8kn',
28096         'otimes',
28097         '8l5',
28098         'perp',
28099         '8m5',
28100         'sdot',
28101         '8o8',
28102         'lceil',
28103         '8o9',
28104         'rceil',
28105         '8oa',
28106         'lfloor',
28107         '8ob',
28108         'rfloor',
28109         '8p9',
28110         'lang',
28111         '8pa',
28112         'rang',
28113         '9ea',
28114         'loz',
28115         '9j0',
28116         'spades',
28117         '9j3',
28118         'clubs',
28119         '9j5',
28120         'hearts',
28121         '9j6',
28122         'diams',
28123         'ai',
28124         'OElig',
28125         'aj',
28126         'oelig',
28127         'b0',
28128         'Scaron',
28129         'b1',
28130         'scaron',
28131         'bo',
28132         'Yuml',
28133         'm6',
28134         'circ',
28135         'ms',
28136         'tilde',
28137         '802',
28138         'ensp',
28139         '803',
28140         'emsp',
28141         '809',
28142         'thinsp',
28143         '80c',
28144         'zwnj',
28145         '80d',
28146         'zwj',
28147         '80e',
28148         'lrm',
28149         '80f',
28150         'rlm',
28151         '80j',
28152         'ndash',
28153         '80k',
28154         'mdash',
28155         '80o',
28156         'lsquo',
28157         '80p',
28158         'rsquo',
28159         '80q',
28160         'sbquo',
28161         '80s',
28162         'ldquo',
28163         '80t',
28164         'rdquo',
28165         '80u',
28166         'bdquo',
28167         '810',
28168         'dagger',
28169         '811',
28170         'Dagger',
28171         '81g',
28172         'permil',
28173         '81p',
28174         'lsaquo',
28175         '81q',
28176         'rsaquo',
28177         '85c',
28178         'euro'
28179     ],
28180
28181          
28182     /**
28183      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28184      *
28185      * @method encodeRaw
28186      * @param {String} text Text to encode.
28187      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28188      * @return {String} Entity encoded text.
28189      */
28190     encodeRaw: function(text, attr)
28191     {
28192         var t = this;
28193         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28194             return t.baseEntities[chr] || chr;
28195         });
28196     },
28197     /**
28198      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28199      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28200      * and is exposed as the DOMUtils.encode function.
28201      *
28202      * @method encodeAllRaw
28203      * @param {String} text Text to encode.
28204      * @return {String} Entity encoded text.
28205      */
28206     encodeAllRaw: function(text) {
28207         var t = this;
28208         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28209             return t.baseEntities[chr] || chr;
28210         });
28211     },
28212     /**
28213      * Encodes the specified string using numeric entities. The core entities will be
28214      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28215      *
28216      * @method encodeNumeric
28217      * @param {String} text Text to encode.
28218      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28219      * @return {String} Entity encoded text.
28220      */
28221     encodeNumeric: function(text, attr) {
28222         var t = this;
28223         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28224             // Multi byte sequence convert it to a single entity
28225             if (chr.length > 1) {
28226                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28227             }
28228             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28229         });
28230     },
28231     /**
28232      * Encodes the specified string using named entities. The core entities will be encoded
28233      * as named ones but all non lower ascii characters will be encoded into named entities.
28234      *
28235      * @method encodeNamed
28236      * @param {String} text Text to encode.
28237      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28238      * @param {Object} entities Optional parameter with entities to use.
28239      * @return {String} Entity encoded text.
28240      */
28241     encodeNamed: function(text, attr, entities) {
28242         var t = this;
28243         entities = entities || this.namedEntities;
28244         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28245             return t.baseEntities[chr] || entities[chr] || chr;
28246         });
28247     },
28248     /**
28249      * Returns an encode function based on the name(s) and it's optional entities.
28250      *
28251      * @method getEncodeFunc
28252      * @param {String} name Comma separated list of encoders for example named,numeric.
28253      * @param {String} entities Optional parameter with entities to use instead of the built in set.
28254      * @return {function} Encode function to be used.
28255      */
28256     getEncodeFunc: function(name, entities) {
28257         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28258         var t = this;
28259         function encodeNamedAndNumeric(text, attr) {
28260             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28261                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28262             });
28263         }
28264
28265         function encodeCustomNamed(text, attr) {
28266             return t.encodeNamed(text, attr, entities);
28267         }
28268         // Replace + with , to be compatible with previous TinyMCE versions
28269         name = this.makeMap(name.replace(/\+/g, ','));
28270         // Named and numeric encoder
28271         if (name.named && name.numeric) {
28272             return this.encodeNamedAndNumeric;
28273         }
28274         // Named encoder
28275         if (name.named) {
28276             // Custom names
28277             if (entities) {
28278                 return encodeCustomNamed;
28279             }
28280             return this.encodeNamed;
28281         }
28282         // Numeric
28283         if (name.numeric) {
28284             return this.encodeNumeric;
28285         }
28286         // Raw encoder
28287         return this.encodeRaw;
28288     },
28289     /**
28290      * Decodes the specified string, this will replace entities with raw UTF characters.
28291      *
28292      * @method decode
28293      * @param {String} text Text to entity decode.
28294      * @return {String} Entity decoded string.
28295      */
28296     decode: function(text)
28297     {
28298         var  t = this;
28299         return text.replace(this.entityRegExp, function(all, numeric) {
28300             if (numeric) {
28301                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28302                 // Support upper UTF
28303                 if (numeric > 65535) {
28304                     numeric -= 65536;
28305                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28306                 }
28307                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28308             }
28309             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28310         });
28311     },
28312     nativeDecode : function (text) {
28313         return text;
28314     },
28315     makeMap : function (items, delim, map) {
28316                 var i;
28317                 items = items || [];
28318                 delim = delim || ',';
28319                 if (typeof items == "string") {
28320                         items = items.split(delim);
28321                 }
28322                 map = map || {};
28323                 i = items.length;
28324                 while (i--) {
28325                         map[items[i]] = {};
28326                 }
28327                 return map;
28328         }
28329 };
28330     
28331     
28332     
28333 Roo.htmleditor.TidyEntities.init();
28334 /**
28335  * @class Roo.htmleditor.KeyEnter
28336  * Handle Enter press..
28337  * @cfg {Roo.HtmlEditorCore} core the editor.
28338  * @constructor
28339  * Create a new Filter.
28340  * @param {Object} config Configuration options
28341  */
28342
28343
28344
28345
28346
28347 Roo.htmleditor.KeyEnter = function(cfg) {
28348     Roo.apply(this, cfg);
28349     // this does not actually call walk as it's really just a abstract class
28350  
28351     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28352 }
28353
28354 //Roo.htmleditor.KeyEnter.i = 0;
28355
28356
28357 Roo.htmleditor.KeyEnter.prototype = {
28358     
28359     core : false,
28360     
28361     keypress : function(e)
28362     {
28363         if (e.charCode != 13 && e.charCode != 10) {
28364             Roo.log([e.charCode,e]);
28365             return true;
28366         }
28367         e.preventDefault();
28368         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28369         var doc = this.core.doc;
28370           //add a new line
28371        
28372     
28373         var sel = this.core.getSelection();
28374         var range = sel.getRangeAt(0);
28375         var n = range.commonAncestorContainer;
28376         var pc = range.closest([ 'ol', 'ul']);
28377         var pli = range.closest('li');
28378         if (!pc || e.ctrlKey) {
28379             // on it list, or ctrl pressed.
28380             if (!e.ctrlKey) {
28381                 sel.insertNode('br', 'after'); 
28382             } else {
28383                 // only do this if we have ctrl key..
28384                 var br = doc.createElement('br');
28385                 br.className = 'clear';
28386                 br.setAttribute('style', 'clear: both');
28387                 sel.insertNode(br, 'after'); 
28388             }
28389             
28390          
28391             this.core.undoManager.addEvent();
28392             this.core.fireEditorEvent(e);
28393             return false;
28394         }
28395         
28396         // deal with <li> insetion
28397         if (pli.innerText.trim() == '' &&
28398             pli.previousSibling &&
28399             pli.previousSibling.nodeName == 'LI' &&
28400             pli.previousSibling.innerText.trim() ==  '') {
28401             pli.parentNode.removeChild(pli.previousSibling);
28402             sel.cursorAfter(pc);
28403             this.core.undoManager.addEvent();
28404             this.core.fireEditorEvent(e);
28405             return false;
28406         }
28407     
28408         var li = doc.createElement('LI');
28409         li.innerHTML = '&nbsp;';
28410         if (!pli || !pli.firstSibling) {
28411             pc.appendChild(li);
28412         } else {
28413             pli.parentNode.insertBefore(li, pli.firstSibling);
28414         }
28415         sel.cursorText (li.firstChild);
28416       
28417         this.core.undoManager.addEvent();
28418         this.core.fireEditorEvent(e);
28419
28420         return false;
28421         
28422     
28423         
28424         
28425          
28426     }
28427 };
28428      
28429 /**
28430  * @class Roo.htmleditor.Block
28431  * Base class for html editor blocks - do not use it directly .. extend it..
28432  * @cfg {DomElement} node The node to apply stuff to.
28433  * @cfg {String} friendly_name the name that appears in the context bar about this block
28434  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28435  
28436  * @constructor
28437  * Create a new Filter.
28438  * @param {Object} config Configuration options
28439  */
28440
28441 Roo.htmleditor.Block  = function(cfg)
28442 {
28443     // do nothing .. should not be called really.
28444 }
28445 /**
28446  * factory method to get the block from an element (using cache if necessary)
28447  * @static
28448  * @param {HtmlElement} the dom element
28449  */
28450 Roo.htmleditor.Block.factory = function(node)
28451 {
28452     var cc = Roo.htmleditor.Block.cache;
28453     var id = Roo.get(node).id;
28454     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28455         Roo.htmleditor.Block.cache[id].readElement(node);
28456         return Roo.htmleditor.Block.cache[id];
28457     }
28458     var db  = node.getAttribute('data-block');
28459     if (!db) {
28460         db = node.nodeName.toLowerCase().toUpperCaseFirst();
28461     }
28462     var cls = Roo.htmleditor['Block' + db];
28463     if (typeof(cls) == 'undefined') {
28464         //Roo.log(node.getAttribute('data-block'));
28465         Roo.log("OOps missing block : " + 'Block' + db);
28466         return false;
28467     }
28468     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
28469     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
28470 };
28471
28472 /**
28473  * initalize all Elements from content that are 'blockable'
28474  * @static
28475  * @param the body element
28476  */
28477 Roo.htmleditor.Block.initAll = function(body, type)
28478 {
28479     if (typeof(type) == 'undefined') {
28480         var ia = Roo.htmleditor.Block.initAll;
28481         ia(body,'table');
28482         ia(body,'td');
28483         ia(body,'figure');
28484         return;
28485     }
28486     Roo.each(Roo.get(body).query(type), function(e) {
28487         Roo.htmleditor.Block.factory(e);    
28488     },this);
28489 };
28490 // question goes here... do we need to clear out this cache sometimes?
28491 // or show we make it relivant to the htmleditor.
28492 Roo.htmleditor.Block.cache = {};
28493
28494 Roo.htmleditor.Block.prototype = {
28495     
28496     node : false,
28497     
28498      // used by context menu
28499     friendly_name : 'Based Block',
28500     
28501     // text for button to delete this element
28502     deleteTitle : false,
28503     
28504     context : false,
28505     /**
28506      * Update a node with values from this object
28507      * @param {DomElement} node
28508      */
28509     updateElement : function(node)
28510     {
28511         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
28512     },
28513      /**
28514      * convert to plain HTML for calling insertAtCursor..
28515      */
28516     toHTML : function()
28517     {
28518         return Roo.DomHelper.markup(this.toObject());
28519     },
28520     /**
28521      * used by readEleemnt to extract data from a node
28522      * may need improving as it's pretty basic
28523      
28524      * @param {DomElement} node
28525      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
28526      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
28527      * @param {String} style the style property - eg. text-align
28528      */
28529     getVal : function(node, tag, attr, style)
28530     {
28531         var n = node;
28532         if (tag !== true && n.tagName != tag.toUpperCase()) {
28533             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
28534             // but kiss for now.
28535             n = node.getElementsByTagName(tag).item(0);
28536         }
28537         if (!n) {
28538             return '';
28539         }
28540         if (attr === false) {
28541             return n;
28542         }
28543         if (attr == 'html') {
28544             return n.innerHTML;
28545         }
28546         if (attr == 'style') {
28547             return n.style[style]; 
28548         }
28549         
28550         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
28551             
28552     },
28553     /**
28554      * create a DomHelper friendly object - for use with 
28555      * Roo.DomHelper.markup / overwrite / etc..
28556      * (override this)
28557      */
28558     toObject : function()
28559     {
28560         return {};
28561     },
28562       /**
28563      * Read a node that has a 'data-block' property - and extract the values from it.
28564      * @param {DomElement} node - the node
28565      */
28566     readElement : function(node)
28567     {
28568         
28569     } 
28570     
28571     
28572 };
28573
28574  
28575
28576 /**
28577  * @class Roo.htmleditor.BlockFigure
28578  * Block that has an image and a figcaption
28579  * @cfg {String} image_src the url for the image
28580  * @cfg {String} align (left|right) alignment for the block default left
28581  * @cfg {String} caption the text to appear below  (and in the alt tag)
28582  * @cfg {String} caption_display (block|none) display or not the caption
28583  * @cfg {String|number} image_width the width of the image number or %?
28584  * @cfg {String|number} image_height the height of the image number or %?
28585  * 
28586  * @constructor
28587  * Create a new Filter.
28588  * @param {Object} config Configuration options
28589  */
28590
28591 Roo.htmleditor.BlockFigure = function(cfg)
28592 {
28593     if (cfg.node) {
28594         this.readElement(cfg.node);
28595         this.updateElement(cfg.node);
28596     }
28597     Roo.apply(this, cfg);
28598 }
28599 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
28600  
28601     
28602     // setable values.
28603     image_src: '',
28604     align: 'center',
28605     caption : '',
28606     caption_display : 'block',
28607     width : '100%',
28608     cls : '',
28609     href: '',
28610     video_url : '',
28611     
28612     // margin: '2%', not used
28613     
28614     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
28615
28616     
28617     // used by context menu
28618     friendly_name : 'Image with caption',
28619     deleteTitle : "Delete Image and Caption",
28620     
28621     contextMenu : function(toolbar)
28622     {
28623         
28624         var block = function() {
28625             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
28626         };
28627         
28628         
28629         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
28630         
28631         var syncValue = toolbar.editorcore.syncValue;
28632         
28633         var fields = {};
28634         
28635         return [
28636              {
28637                 xtype : 'TextItem',
28638                 text : "Source: ",
28639                 xns : rooui.Toolbar  //Boostrap?
28640             },
28641             {
28642                 xtype : 'Button',
28643                 text: 'Change Image URL',
28644                  
28645                 listeners : {
28646                     click: function (btn, state)
28647                     {
28648                         var b = block();
28649                         
28650                         Roo.MessageBox.show({
28651                             title : "Image Source URL",
28652                             msg : "Enter the url for the image",
28653                             buttons: Roo.MessageBox.OKCANCEL,
28654                             fn: function(btn, val){
28655                                 if (btn != 'ok') {
28656                                     return;
28657                                 }
28658                                 b.image_src = val;
28659                                 b.updateElement();
28660                                 syncValue();
28661                                 toolbar.editorcore.onEditorEvent();
28662                             },
28663                             minWidth:250,
28664                             prompt:true,
28665                             //multiline: multiline,
28666                             modal : true,
28667                             value : b.image_src
28668                         });
28669                     }
28670                 },
28671                 xns : rooui.Toolbar
28672             },
28673          
28674             {
28675                 xtype : 'Button',
28676                 text: 'Change Link URL',
28677                  
28678                 listeners : {
28679                     click: function (btn, state)
28680                     {
28681                         var b = block();
28682                         
28683                         Roo.MessageBox.show({
28684                             title : "Link URL",
28685                             msg : "Enter the url for the link - leave blank to have no link",
28686                             buttons: Roo.MessageBox.OKCANCEL,
28687                             fn: function(btn, val){
28688                                 if (btn != 'ok') {
28689                                     return;
28690                                 }
28691                                 b.href = val;
28692                                 b.updateElement();
28693                                 syncValue();
28694                                 toolbar.editorcore.onEditorEvent();
28695                             },
28696                             minWidth:250,
28697                             prompt:true,
28698                             //multiline: multiline,
28699                             modal : true,
28700                             value : b.href
28701                         });
28702                     }
28703                 },
28704                 xns : rooui.Toolbar
28705             },
28706             {
28707                 xtype : 'Button',
28708                 text: 'Show Video URL',
28709                  
28710                 listeners : {
28711                     click: function (btn, state)
28712                     {
28713                         Roo.MessageBox.alert("Video URL",
28714                             block().video_url == '' ? 'This image is not linked ot a video' :
28715                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
28716                     }
28717                 },
28718                 xns : rooui.Toolbar
28719             },
28720             
28721             
28722             {
28723                 xtype : 'TextItem',
28724                 text : "Width: ",
28725                 xns : rooui.Toolbar  //Boostrap?
28726             },
28727             {
28728                 xtype : 'ComboBox',
28729                 allowBlank : false,
28730                 displayField : 'val',
28731                 editable : true,
28732                 listWidth : 100,
28733                 triggerAction : 'all',
28734                 typeAhead : true,
28735                 valueField : 'val',
28736                 width : 70,
28737                 name : 'width',
28738                 listeners : {
28739                     select : function (combo, r, index)
28740                     {
28741                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28742                         var b = block();
28743                         b.width = r.get('val');
28744                         b.updateElement();
28745                         syncValue();
28746                         toolbar.editorcore.onEditorEvent();
28747                     }
28748                 },
28749                 xns : rooui.form,
28750                 store : {
28751                     xtype : 'SimpleStore',
28752                     data : [
28753                         ['100%'],
28754                         ['80%'],
28755                         ['50%'],
28756                         ['20%'],
28757                         ['10%']
28758                     ],
28759                     fields : [ 'val'],
28760                     xns : Roo.data
28761                 }
28762             },
28763             {
28764                 xtype : 'TextItem',
28765                 text : "Align: ",
28766                 xns : rooui.Toolbar  //Boostrap?
28767             },
28768             {
28769                 xtype : 'ComboBox',
28770                 allowBlank : false,
28771                 displayField : 'val',
28772                 editable : true,
28773                 listWidth : 100,
28774                 triggerAction : 'all',
28775                 typeAhead : true,
28776                 valueField : 'val',
28777                 width : 70,
28778                 name : 'align',
28779                 listeners : {
28780                     select : function (combo, r, index)
28781                     {
28782                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28783                         var b = block();
28784                         b.align = r.get('val');
28785                         b.updateElement();
28786                         syncValue();
28787                         toolbar.editorcore.onEditorEvent();
28788                     }
28789                 },
28790                 xns : rooui.form,
28791                 store : {
28792                     xtype : 'SimpleStore',
28793                     data : [
28794                         ['left'],
28795                         ['right'],
28796                         ['center']
28797                     ],
28798                     fields : [ 'val'],
28799                     xns : Roo.data
28800                 }
28801             },
28802             
28803             
28804             {
28805                 xtype : 'Button',
28806                 text: 'Hide Caption',
28807                 name : 'caption_display',
28808                 pressed : false,
28809                 enableToggle : true,
28810                 setValue : function(v) {
28811                     // this trigger toggle.
28812                      
28813                     this.setText(v ? "Hide Caption" : "Show Caption");
28814                     this.setPressed(v != 'block');
28815                 },
28816                 listeners : {
28817                     toggle: function (btn, state)
28818                     {
28819                         var b  = block();
28820                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
28821                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
28822                         b.updateElement();
28823                         syncValue();
28824                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28825                         toolbar.editorcore.onEditorEvent();
28826                     }
28827                 },
28828                 xns : rooui.Toolbar
28829             }
28830         ];
28831         
28832     },
28833     /**
28834      * create a DomHelper friendly object - for use with
28835      * Roo.DomHelper.markup / overwrite / etc..
28836      */
28837     toObject : function()
28838     {
28839         var d = document.createElement('div');
28840         d.innerHTML = this.caption;
28841         
28842         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
28843         
28844         var iw = this.align == 'center' ? this.width : '100%';
28845         var img =   {
28846             tag : 'img',
28847             contenteditable : 'false',
28848             src : this.image_src,
28849             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
28850             style: {
28851                 width : iw,
28852                 maxWidth : iw + ' !important', // this is not getting rendered?
28853                 margin : m  
28854                 
28855             }
28856         };
28857         /*
28858         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
28859                     '<a href="{2}">' + 
28860                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
28861                     '</a>' + 
28862                 '</div>',
28863         */
28864                 
28865         if (this.href.length > 0) {
28866             img = {
28867                 tag : 'a',
28868                 href: this.href,
28869                 contenteditable : 'true',
28870                 cn : [
28871                     img
28872                 ]
28873             };
28874         }
28875         
28876         
28877         if (this.video_url.length > 0) {
28878             img = {
28879                 tag : 'div',
28880                 cls : this.cls,
28881                 frameborder : 0,
28882                 allowfullscreen : true,
28883                 width : 420,  // these are for video tricks - that we replace the outer
28884                 height : 315,
28885                 src : this.video_url,
28886                 cn : [
28887                     img
28888                 ]
28889             };
28890         }
28891         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
28892         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
28893         
28894   
28895         var ret =   {
28896             tag: 'figure',
28897             'data-block' : 'Figure',
28898             'data-width' : this.width, 
28899             contenteditable : 'false',
28900             
28901             style : {
28902                 display: 'block',
28903                 float :  this.align ,
28904                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
28905                 width : this.align == 'center' ? '100%' : this.width,
28906                 margin:  '0px',
28907                 padding: this.align == 'center' ? '0' : '0 10px' ,
28908                 textAlign : this.align   // seems to work for email..
28909                 
28910             },
28911            
28912             
28913             align : this.align,
28914             cn : [
28915                 img,
28916               
28917                 {
28918                     tag: 'figcaption',
28919                     'data-display' : this.caption_display,
28920                     style : {
28921                         textAlign : 'left',
28922                         fontSize : '16px',
28923                         lineHeight : '24px',
28924                         display : this.caption_display,
28925                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
28926                         margin: m,
28927                         width: this.align == 'center' ?  this.width : '100%' 
28928                     
28929                          
28930                     },
28931                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
28932                     cn : [
28933                         {
28934                             tag: 'div',
28935                             style  : {
28936                                 marginTop : '16px',
28937                                 textAlign : 'left'
28938                             },
28939                             align: 'left',
28940                             cn : [
28941                                 {
28942                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
28943                                     tag : 'i',
28944                                     contenteditable : true,
28945                                     html : captionhtml
28946                                 }
28947                                 
28948                             ]
28949                         }
28950                         
28951                     ]
28952                     
28953                 }
28954             ]
28955         };
28956         return ret;
28957          
28958     },
28959     
28960     readElement : function(node)
28961     {
28962         // this should not really come from the link...
28963         this.video_url = this.getVal(node, 'div', 'src');
28964         this.cls = this.getVal(node, 'div', 'class');
28965         this.href = this.getVal(node, 'a', 'href');
28966         
28967         
28968         this.image_src = this.getVal(node, 'img', 'src');
28969          
28970         this.align = this.getVal(node, 'figure', 'align');
28971         var figcaption = this.getVal(node, 'figcaption', false);
28972         if (figcaption !== '') {
28973             this.caption = this.getVal(figcaption, 'i', 'html');
28974         }
28975         
28976
28977         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
28978         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
28979         this.width = this.getVal(node, true, 'data-width');
28980         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
28981         
28982     },
28983     removeNode : function()
28984     {
28985         return this.node;
28986     }
28987     
28988   
28989    
28990      
28991     
28992     
28993     
28994     
28995 })
28996
28997  
28998
28999 /**
29000  * @class Roo.htmleditor.BlockTable
29001  * Block that manages a table
29002  * 
29003  * @constructor
29004  * Create a new Filter.
29005  * @param {Object} config Configuration options
29006  */
29007
29008 Roo.htmleditor.BlockTable = function(cfg)
29009 {
29010     if (cfg.node) {
29011         this.readElement(cfg.node);
29012         this.updateElement(cfg.node);
29013     }
29014     Roo.apply(this, cfg);
29015     if (!cfg.node) {
29016         this.rows = [];
29017         for(var r = 0; r < this.no_row; r++) {
29018             this.rows[r] = [];
29019             for(var c = 0; c < this.no_col; c++) {
29020                 this.rows[r][c] = this.emptyCell();
29021             }
29022         }
29023     }
29024     
29025     
29026 }
29027 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29028  
29029     rows : false,
29030     no_col : 1,
29031     no_row : 1,
29032     
29033     
29034     width: '100%',
29035     
29036     // used by context menu
29037     friendly_name : 'Table',
29038     deleteTitle : 'Delete Table',
29039     // context menu is drawn once..
29040     
29041     contextMenu : function(toolbar)
29042     {
29043         
29044         var block = function() {
29045             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29046         };
29047         
29048         
29049         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29050         
29051         var syncValue = toolbar.editorcore.syncValue;
29052         
29053         var fields = {};
29054         
29055         return [
29056             {
29057                 xtype : 'TextItem',
29058                 text : "Width: ",
29059                 xns : rooui.Toolbar  //Boostrap?
29060             },
29061             {
29062                 xtype : 'ComboBox',
29063                 allowBlank : false,
29064                 displayField : 'val',
29065                 editable : true,
29066                 listWidth : 100,
29067                 triggerAction : 'all',
29068                 typeAhead : true,
29069                 valueField : 'val',
29070                 width : 100,
29071                 name : 'width',
29072                 listeners : {
29073                     select : function (combo, r, index)
29074                     {
29075                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29076                         var b = block();
29077                         b.width = r.get('val');
29078                         b.updateElement();
29079                         syncValue();
29080                         toolbar.editorcore.onEditorEvent();
29081                     }
29082                 },
29083                 xns : rooui.form,
29084                 store : {
29085                     xtype : 'SimpleStore',
29086                     data : [
29087                         ['100%'],
29088                         ['auto']
29089                     ],
29090                     fields : [ 'val'],
29091                     xns : Roo.data
29092                 }
29093             },
29094             // -------- Cols
29095             
29096             {
29097                 xtype : 'TextItem',
29098                 text : "Columns: ",
29099                 xns : rooui.Toolbar  //Boostrap?
29100             },
29101          
29102             {
29103                 xtype : 'Button',
29104                 text: '-',
29105                 listeners : {
29106                     click : function (_self, e)
29107                     {
29108                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29109                         block().removeColumn();
29110                         syncValue();
29111                         toolbar.editorcore.onEditorEvent();
29112                     }
29113                 },
29114                 xns : rooui.Toolbar
29115             },
29116             {
29117                 xtype : 'Button',
29118                 text: '+',
29119                 listeners : {
29120                     click : function (_self, e)
29121                     {
29122                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29123                         block().addColumn();
29124                         syncValue();
29125                         toolbar.editorcore.onEditorEvent();
29126                     }
29127                 },
29128                 xns : rooui.Toolbar
29129             },
29130             // -------- ROWS
29131             {
29132                 xtype : 'TextItem',
29133                 text : "Rows: ",
29134                 xns : rooui.Toolbar  //Boostrap?
29135             },
29136          
29137             {
29138                 xtype : 'Button',
29139                 text: '-',
29140                 listeners : {
29141                     click : function (_self, e)
29142                     {
29143                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29144                         block().removeRow();
29145                         syncValue();
29146                         toolbar.editorcore.onEditorEvent();
29147                     }
29148                 },
29149                 xns : rooui.Toolbar
29150             },
29151             {
29152                 xtype : 'Button',
29153                 text: '+',
29154                 listeners : {
29155                     click : function (_self, e)
29156                     {
29157                         block().addRow();
29158                         syncValue();
29159                         toolbar.editorcore.onEditorEvent();
29160                     }
29161                 },
29162                 xns : rooui.Toolbar
29163             },
29164             // -------- ROWS
29165             {
29166                 xtype : 'Button',
29167                 text: 'Reset Column Widths',
29168                 listeners : {
29169                     
29170                     click : function (_self, e)
29171                     {
29172                         block().resetWidths();
29173                         syncValue();
29174                         toolbar.editorcore.onEditorEvent();
29175                     }
29176                 },
29177                 xns : rooui.Toolbar
29178             } 
29179             
29180             
29181             
29182         ];
29183         
29184     },
29185     
29186     
29187   /**
29188      * create a DomHelper friendly object - for use with
29189      * Roo.DomHelper.markup / overwrite / etc..
29190      * ?? should it be called with option to hide all editing features?
29191      */
29192     toObject : function()
29193     {
29194         
29195         var ret = {
29196             tag : 'table',
29197             contenteditable : 'false', // this stops cell selection from picking the table.
29198             'data-block' : 'Table',
29199             style : {
29200                 width:  this.width,
29201                 border : 'solid 1px #000', // ??? hard coded?
29202                 'border-collapse' : 'collapse' 
29203             },
29204             cn : [
29205                 { tag : 'tbody' , cn : [] }
29206             ]
29207         };
29208         
29209         // do we have a head = not really 
29210         var ncols = 0;
29211         Roo.each(this.rows, function( row ) {
29212             var tr = {
29213                 tag: 'tr',
29214                 style : {
29215                     margin: '6px',
29216                     border : 'solid 1px #000',
29217                     textAlign : 'left' 
29218                 },
29219                 cn : [ ]
29220             };
29221             
29222             ret.cn[0].cn.push(tr);
29223             // does the row have any properties? ?? height?
29224             var nc = 0;
29225             Roo.each(row, function( cell ) {
29226                 
29227                 var td = {
29228                     tag : 'td',
29229                     contenteditable :  'true',
29230                     'data-block' : 'Td',
29231                     html : cell.html,
29232                     style : cell.style
29233                 };
29234                 if (cell.colspan > 1) {
29235                     td.colspan = cell.colspan ;
29236                     nc += cell.colspan;
29237                 } else {
29238                     nc++;
29239                 }
29240                 if (cell.rowspan > 1) {
29241                     td.rowspan = cell.rowspan ;
29242                 }
29243                 
29244                 
29245                 // widths ?
29246                 tr.cn.push(td);
29247                     
29248                 
29249             }, this);
29250             ncols = Math.max(nc, ncols);
29251             
29252             
29253         }, this);
29254         // add the header row..
29255         
29256         ncols++;
29257          
29258         
29259         return ret;
29260          
29261     },
29262     
29263     readElement : function(node)
29264     {
29265         node  = node ? node : this.node ;
29266         this.width = this.getVal(node, true, 'style', 'width') || '100%';
29267         
29268         this.rows = [];
29269         this.no_row = 0;
29270         var trs = Array.from(node.rows);
29271         trs.forEach(function(tr) {
29272             var row =  [];
29273             this.rows.push(row);
29274             
29275             this.no_row++;
29276             var no_column = 0;
29277             Array.from(tr.cells).forEach(function(td) {
29278                 
29279                 var add = {
29280                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29281                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29282                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29283                     html : td.innerHTML
29284                 };
29285                 no_column += add.colspan;
29286                      
29287                 
29288                 row.push(add);
29289                 
29290                 
29291             },this);
29292             this.no_col = Math.max(this.no_col, no_column);
29293             
29294             
29295         },this);
29296         
29297         
29298     },
29299     normalizeRows: function()
29300     {
29301         var ret= [];
29302         var rid = -1;
29303         this.rows.forEach(function(row) {
29304             rid++;
29305             ret[rid] = [];
29306             row = this.normalizeRow(row);
29307             var cid = 0;
29308             row.forEach(function(c) {
29309                 while (typeof(ret[rid][cid]) != 'undefined') {
29310                     cid++;
29311                 }
29312                 if (typeof(ret[rid]) == 'undefined') {
29313                     ret[rid] = [];
29314                 }
29315                 ret[rid][cid] = c;
29316                 c.row = rid;
29317                 c.col = cid;
29318                 if (c.rowspan < 2) {
29319                     return;
29320                 }
29321                 
29322                 for(var i = 1 ;i < c.rowspan; i++) {
29323                     if (typeof(ret[rid+i]) == 'undefined') {
29324                         ret[rid+i] = [];
29325                     }
29326                     ret[rid+i][cid] = c;
29327                 }
29328             });
29329         }, this);
29330         return ret;
29331     
29332     },
29333     
29334     normalizeRow: function(row)
29335     {
29336         var ret= [];
29337         row.forEach(function(c) {
29338             if (c.colspan < 2) {
29339                 ret.push(c);
29340                 return;
29341             }
29342             for(var i =0 ;i < c.colspan; i++) {
29343                 ret.push(c);
29344             }
29345         });
29346         return ret;
29347     
29348     },
29349     
29350     deleteColumn : function(sel)
29351     {
29352         if (!sel || sel.type != 'col') {
29353             return;
29354         }
29355         if (this.no_col < 2) {
29356             return;
29357         }
29358         
29359         this.rows.forEach(function(row) {
29360             var cols = this.normalizeRow(row);
29361             var col = cols[sel.col];
29362             if (col.colspan > 1) {
29363                 col.colspan --;
29364             } else {
29365                 row.remove(col);
29366             }
29367             
29368         }, this);
29369         this.no_col--;
29370         
29371     },
29372     removeColumn : function()
29373     {
29374         this.deleteColumn({
29375             type: 'col',
29376             col : this.no_col-1
29377         });
29378         this.updateElement();
29379     },
29380     
29381      
29382     addColumn : function()
29383     {
29384         
29385         this.rows.forEach(function(row) {
29386             row.push(this.emptyCell());
29387            
29388         }, this);
29389         this.updateElement();
29390     },
29391     
29392     deleteRow : function(sel)
29393     {
29394         if (!sel || sel.type != 'row') {
29395             return;
29396         }
29397         
29398         if (this.no_row < 2) {
29399             return;
29400         }
29401         
29402         var rows = this.normalizeRows();
29403         
29404         
29405         rows[sel.row].forEach(function(col) {
29406             if (col.rowspan > 1) {
29407                 col.rowspan--;
29408             } else {
29409                 col.remove = 1; // flage it as removed.
29410             }
29411             
29412         }, this);
29413         var newrows = [];
29414         this.rows.forEach(function(row) {
29415             newrow = [];
29416             row.forEach(function(c) {
29417                 if (typeof(c.remove) == 'undefined') {
29418                     newrow.push(c);
29419                 }
29420                 
29421             });
29422             if (newrow.length > 0) {
29423                 newrows.push(row);
29424             }
29425         });
29426         this.rows =  newrows;
29427         
29428         
29429         
29430         this.no_row--;
29431         this.updateElement();
29432         
29433     },
29434     removeRow : function()
29435     {
29436         this.deleteRow({
29437             type: 'row',
29438             row : this.no_row-1
29439         });
29440         
29441     },
29442     
29443      
29444     addRow : function()
29445     {
29446         
29447         var row = [];
29448         for (var i = 0; i < this.no_col; i++ ) {
29449             
29450             row.push(this.emptyCell());
29451            
29452         }
29453         this.rows.push(row);
29454         this.updateElement();
29455         
29456     },
29457      
29458     // the default cell object... at present...
29459     emptyCell : function() {
29460         return (new Roo.htmleditor.BlockTd({})).toObject();
29461         
29462      
29463     },
29464     
29465     removeNode : function()
29466     {
29467         return this.node;
29468     },
29469     
29470     
29471     
29472     resetWidths : function()
29473     {
29474         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
29475             var nn = Roo.htmleditor.Block.factory(n);
29476             nn.width = '';
29477             nn.updateElement(n);
29478         });
29479     }
29480     
29481     
29482     
29483     
29484 })
29485
29486 /**
29487  *
29488  * editing a TD?
29489  *
29490  * since selections really work on the table cell, then editing really should work from there
29491  *
29492  * The original plan was to support merging etc... - but that may not be needed yet..
29493  *
29494  * So this simple version will support:
29495  *   add/remove cols
29496  *   adjust the width +/-
29497  *   reset the width...
29498  *   
29499  *
29500  */
29501
29502
29503  
29504
29505 /**
29506  * @class Roo.htmleditor.BlockTable
29507  * Block that manages a table
29508  * 
29509  * @constructor
29510  * Create a new Filter.
29511  * @param {Object} config Configuration options
29512  */
29513
29514 Roo.htmleditor.BlockTd = function(cfg)
29515 {
29516     if (cfg.node) {
29517         this.readElement(cfg.node);
29518         this.updateElement(cfg.node);
29519     }
29520     Roo.apply(this, cfg);
29521      
29522     
29523     
29524 }
29525 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
29526  
29527     node : false,
29528     
29529     width: '',
29530     textAlign : 'left',
29531     valign : 'top',
29532     
29533     colspan : 1,
29534     rowspan : 1,
29535     
29536     
29537     // used by context menu
29538     friendly_name : 'Table Cell',
29539     deleteTitle : false, // use our customer delete
29540     
29541     // context menu is drawn once..
29542     
29543     contextMenu : function(toolbar)
29544     {
29545         
29546         var cell = function() {
29547             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29548         };
29549         
29550         var table = function() {
29551             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
29552         };
29553         
29554         var lr = false;
29555         var saveSel = function()
29556         {
29557             lr = toolbar.editorcore.getSelection().getRangeAt(0);
29558         }
29559         var restoreSel = function()
29560         {
29561             if (lr) {
29562                 (function() {
29563                     toolbar.editorcore.focus();
29564                     var cr = toolbar.editorcore.getSelection();
29565                     cr.removeAllRanges();
29566                     cr.addRange(lr);
29567                     toolbar.editorcore.onEditorEvent();
29568                 }).defer(10, this);
29569                 
29570                 
29571             }
29572         }
29573         
29574         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29575         
29576         var syncValue = toolbar.editorcore.syncValue;
29577         
29578         var fields = {};
29579         
29580         return [
29581             {
29582                 xtype : 'Button',
29583                 text : 'Edit Table',
29584                 listeners : {
29585                     click : function() {
29586                         var t = toolbar.tb.selectedNode.closest('table');
29587                         toolbar.editorcore.selectNode(t);
29588                         toolbar.editorcore.onEditorEvent();                        
29589                     }
29590                 }
29591                 
29592             },
29593               
29594            
29595              
29596             {
29597                 xtype : 'TextItem',
29598                 text : "Column Width: ",
29599                  xns : rooui.Toolbar 
29600                
29601             },
29602             {
29603                 xtype : 'Button',
29604                 text: '-',
29605                 listeners : {
29606                     click : function (_self, e)
29607                     {
29608                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29609                         cell().shrinkColumn();
29610                         syncValue();
29611                          toolbar.editorcore.onEditorEvent();
29612                     }
29613                 },
29614                 xns : rooui.Toolbar
29615             },
29616             {
29617                 xtype : 'Button',
29618                 text: '+',
29619                 listeners : {
29620                     click : function (_self, e)
29621                     {
29622                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29623                         cell().growColumn();
29624                         syncValue();
29625                         toolbar.editorcore.onEditorEvent();
29626                     }
29627                 },
29628                 xns : rooui.Toolbar
29629             },
29630             
29631             {
29632                 xtype : 'TextItem',
29633                 text : "Vertical Align: ",
29634                 xns : rooui.Toolbar  //Boostrap?
29635             },
29636             {
29637                 xtype : 'ComboBox',
29638                 allowBlank : false,
29639                 displayField : 'val',
29640                 editable : true,
29641                 listWidth : 100,
29642                 triggerAction : 'all',
29643                 typeAhead : true,
29644                 valueField : 'val',
29645                 width : 100,
29646                 name : 'valign',
29647                 listeners : {
29648                     select : function (combo, r, index)
29649                     {
29650                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29651                         var b = cell();
29652                         b.valign = r.get('val');
29653                         b.updateElement();
29654                         syncValue();
29655                         toolbar.editorcore.onEditorEvent();
29656                     }
29657                 },
29658                 xns : rooui.form,
29659                 store : {
29660                     xtype : 'SimpleStore',
29661                     data : [
29662                         ['top'],
29663                         ['middle'],
29664                         ['bottom'] // there are afew more... 
29665                     ],
29666                     fields : [ 'val'],
29667                     xns : Roo.data
29668                 }
29669             },
29670             
29671             {
29672                 xtype : 'TextItem',
29673                 text : "Merge Cells: ",
29674                  xns : rooui.Toolbar 
29675                
29676             },
29677             
29678             
29679             {
29680                 xtype : 'Button',
29681                 text: 'Right',
29682                 listeners : {
29683                     click : function (_self, e)
29684                     {
29685                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29686                         cell().mergeRight();
29687                         //block().growColumn();
29688                         syncValue();
29689                         toolbar.editorcore.onEditorEvent();
29690                     }
29691                 },
29692                 xns : rooui.Toolbar
29693             },
29694              
29695             {
29696                 xtype : 'Button',
29697                 text: 'Below',
29698                 listeners : {
29699                     click : function (_self, e)
29700                     {
29701                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29702                         cell().mergeBelow();
29703                         //block().growColumn();
29704                         syncValue();
29705                         toolbar.editorcore.onEditorEvent();
29706                     }
29707                 },
29708                 xns : rooui.Toolbar
29709             },
29710             {
29711                 xtype : 'TextItem',
29712                 text : "| ",
29713                  xns : rooui.Toolbar 
29714                
29715             },
29716             
29717             {
29718                 xtype : 'Button',
29719                 text: 'Split',
29720                 listeners : {
29721                     click : function (_self, e)
29722                     {
29723                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29724                         cell().split();
29725                         syncValue();
29726                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29727                         toolbar.editorcore.onEditorEvent();
29728                                              
29729                     }
29730                 },
29731                 xns : rooui.Toolbar
29732             },
29733             {
29734                 xtype : 'Fill',
29735                 xns : rooui.Toolbar 
29736                
29737             },
29738         
29739           
29740             {
29741                 xtype : 'Button',
29742                 text: 'Delete',
29743                  
29744                 xns : rooui.Toolbar,
29745                 menu : {
29746                     xtype : 'Menu',
29747                     xns : rooui.menu,
29748                     items : [
29749                         {
29750                             xtype : 'Item',
29751                             html: 'Column',
29752                             listeners : {
29753                                 click : function (_self, e)
29754                                 {
29755                                     var t = table();
29756                                     
29757                                     cell().deleteColumn();
29758                                     syncValue();
29759                                     toolbar.editorcore.selectNode(t.node);
29760                                     toolbar.editorcore.onEditorEvent();   
29761                                 }
29762                             },
29763                             xns : rooui.menu
29764                         },
29765                         {
29766                             xtype : 'Item',
29767                             html: 'Row',
29768                             listeners : {
29769                                 click : function (_self, e)
29770                                 {
29771                                     var t = table();
29772                                     cell().deleteRow();
29773                                     syncValue();
29774                                     
29775                                     toolbar.editorcore.selectNode(t.node);
29776                                     toolbar.editorcore.onEditorEvent();   
29777                                                          
29778                                 }
29779                             },
29780                             xns : rooui.menu
29781                         },
29782                        {
29783                             xtype : 'Separator',
29784                             xns : rooui.menu
29785                         },
29786                         {
29787                             xtype : 'Item',
29788                             html: 'Table',
29789                             listeners : {
29790                                 click : function (_self, e)
29791                                 {
29792                                     var t = table();
29793                                     var nn = t.node.nextSibling || t.node.previousSibling;
29794                                     t.node.parentNode.removeChild(t.node);
29795                                     if (nn) { 
29796                                         toolbar.editorcore.selectNode(nn, true);
29797                                     }
29798                                     toolbar.editorcore.onEditorEvent();   
29799                                                          
29800                                 }
29801                             },
29802                             xns : rooui.menu
29803                         }
29804                     ]
29805                 }
29806             }
29807             
29808             // align... << fixme
29809             
29810         ];
29811         
29812     },
29813     
29814     
29815   /**
29816      * create a DomHelper friendly object - for use with
29817      * Roo.DomHelper.markup / overwrite / etc..
29818      * ?? should it be called with option to hide all editing features?
29819      */
29820  /**
29821      * create a DomHelper friendly object - for use with
29822      * Roo.DomHelper.markup / overwrite / etc..
29823      * ?? should it be called with option to hide all editing features?
29824      */
29825     toObject : function()
29826     {
29827         var ret = {
29828             tag : 'td',
29829             contenteditable : 'true', // this stops cell selection from picking the table.
29830             'data-block' : 'Td',
29831             valign : this.valign,
29832             style : {  
29833                 'text-align' :  this.textAlign,
29834                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
29835                 'border-collapse' : 'collapse',
29836                 padding : '6px', // 8 for desktop / 4 for mobile
29837                 'vertical-align': this.valign
29838             },
29839             html : this.html
29840         };
29841         if (this.width != '') {
29842             ret.width = this.width;
29843             ret.style.width = this.width;
29844         }
29845         
29846         
29847         if (this.colspan > 1) {
29848             ret.colspan = this.colspan ;
29849         } 
29850         if (this.rowspan > 1) {
29851             ret.rowspan = this.rowspan ;
29852         }
29853         
29854            
29855         
29856         return ret;
29857          
29858     },
29859     
29860     readElement : function(node)
29861     {
29862         node  = node ? node : this.node ;
29863         this.width = node.style.width;
29864         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
29865         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
29866         this.html = node.innerHTML;
29867         if (node.style.textAlign != '') {
29868             this.textAlign = node.style.textAlign;
29869         }
29870         
29871         
29872     },
29873      
29874     // the default cell object... at present...
29875     emptyCell : function() {
29876         return {
29877             colspan :  1,
29878             rowspan :  1,
29879             textAlign : 'left',
29880             html : "&nbsp;" // is this going to be editable now?
29881         };
29882      
29883     },
29884     
29885     removeNode : function()
29886     {
29887         return this.node.closest('table');
29888          
29889     },
29890     
29891     cellData : false,
29892     
29893     colWidths : false,
29894     
29895     toTableArray  : function()
29896     {
29897         var ret = [];
29898         var tab = this.node.closest('tr').closest('table');
29899         Array.from(tab.rows).forEach(function(r, ri){
29900             ret[ri] = [];
29901         });
29902         var rn = 0;
29903         this.colWidths = [];
29904         var all_auto = true;
29905         Array.from(tab.rows).forEach(function(r, ri){
29906             
29907             var cn = 0;
29908             Array.from(r.cells).forEach(function(ce, ci){
29909                 var c =  {
29910                     cell : ce,
29911                     row : rn,
29912                     col: cn,
29913                     colspan : ce.colSpan,
29914                     rowspan : ce.rowSpan
29915                 };
29916                 if (ce.isEqualNode(this.node)) {
29917                     this.cellData = c;
29918                 }
29919                 // if we have been filled up by a row?
29920                 if (typeof(ret[rn][cn]) != 'undefined') {
29921                     while(typeof(ret[rn][cn]) != 'undefined') {
29922                         cn++;
29923                     }
29924                     c.col = cn;
29925                 }
29926                 
29927                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
29928                     this.colWidths[cn] =   ce.style.width;
29929                     if (this.colWidths[cn] != '') {
29930                         all_auto = false;
29931                     }
29932                 }
29933                 
29934                 
29935                 if (c.colspan < 2 && c.rowspan < 2 ) {
29936                     ret[rn][cn] = c;
29937                     cn++;
29938                     return;
29939                 }
29940                 for(var j = 0; j < c.rowspan; j++) {
29941                     if (typeof(ret[rn+j]) == 'undefined') {
29942                         continue; // we have a problem..
29943                     }
29944                     ret[rn+j][cn] = c;
29945                     for(var i = 0; i < c.colspan; i++) {
29946                         ret[rn+j][cn+i] = c;
29947                     }
29948                 }
29949                 
29950                 cn += c.colspan;
29951             }, this);
29952             rn++;
29953         }, this);
29954         
29955         // initalize widths.?
29956         // either all widths or no widths..
29957         if (all_auto) {
29958             this.colWidths[0] = false; // no widths flag.
29959         }
29960         
29961         
29962         return ret;
29963         
29964     },
29965     
29966     
29967     
29968     
29969     mergeRight: function()
29970     {
29971          
29972         // get the contents of the next cell along..
29973         var tr = this.node.closest('tr');
29974         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
29975         if (i >= tr.childNodes.length - 1) {
29976             return; // no cells on right to merge with.
29977         }
29978         var table = this.toTableArray();
29979         
29980         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
29981             return; // nothing right?
29982         }
29983         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
29984         // right cell - must be same rowspan and on the same row.
29985         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
29986             return; // right hand side is not same rowspan.
29987         }
29988         
29989         
29990         
29991         this.node.innerHTML += ' ' + rc.cell.innerHTML;
29992         tr.removeChild(rc.cell);
29993         this.colspan += rc.colspan;
29994         this.node.setAttribute('colspan', this.colspan);
29995
29996         var table = this.toTableArray();
29997         this.normalizeWidths(table);
29998         this.updateWidths(table);
29999     },
30000     
30001     
30002     mergeBelow : function()
30003     {
30004         var table = this.toTableArray();
30005         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30006             return; // no row below
30007         }
30008         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30009             return; // nothing right?
30010         }
30011         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30012         
30013         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30014             return; // right hand side is not same rowspan.
30015         }
30016         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
30017         rc.cell.parentNode.removeChild(rc.cell);
30018         this.rowspan += rc.rowspan;
30019         this.node.setAttribute('rowspan', this.rowspan);
30020     },
30021     
30022     split: function()
30023     {
30024         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30025             return;
30026         }
30027         var table = this.toTableArray();
30028         var cd = this.cellData;
30029         this.rowspan = 1;
30030         this.colspan = 1;
30031         
30032         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30033              
30034             
30035             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30036                 if (r == cd.row && c == cd.col) {
30037                     this.node.removeAttribute('rowspan');
30038                     this.node.removeAttribute('colspan');
30039                 }
30040                  
30041                 var ntd = this.node.cloneNode(); // which col/row should be 0..
30042                 ntd.removeAttribute('id'); 
30043                 ntd.style.width  = this.colWidths[c];
30044                 ntd.innerHTML = '';
30045                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
30046             }
30047             
30048         }
30049         this.redrawAllCells(table);
30050         
30051     },
30052     
30053     
30054     
30055     redrawAllCells: function(table)
30056     {
30057         
30058          
30059         var tab = this.node.closest('tr').closest('table');
30060         var ctr = tab.rows[0].parentNode;
30061         Array.from(tab.rows).forEach(function(r, ri){
30062             
30063             Array.from(r.cells).forEach(function(ce, ci){
30064                 ce.parentNode.removeChild(ce);
30065             });
30066             r.parentNode.removeChild(r);
30067         });
30068         for(var r = 0 ; r < table.length; r++) {
30069             var re = tab.rows[r];
30070             
30071             var re = tab.ownerDocument.createElement('tr');
30072             ctr.appendChild(re);
30073             for(var c = 0 ; c < table[r].length; c++) {
30074                 if (table[r][c].cell === false) {
30075                     continue;
30076                 }
30077                 
30078                 re.appendChild(table[r][c].cell);
30079                  
30080                 table[r][c].cell = false;
30081             }
30082         }
30083         
30084     },
30085     updateWidths : function(table)
30086     {
30087         for(var r = 0 ; r < table.length; r++) {
30088            
30089             for(var c = 0 ; c < table[r].length; c++) {
30090                 if (table[r][c].cell === false) {
30091                     continue;
30092                 }
30093                 
30094                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30095                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30096                     el.width = Math.floor(this.colWidths[c])  +'%';
30097                     el.updateElement(el.node);
30098                 }
30099                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30100                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30101                     var width = 0;
30102                     for(var i = 0; i < table[r][c].colspan; i ++) {
30103                         width += Math.floor(this.colWidths[c + i]);
30104                     }
30105                     el.width = width  +'%';
30106                     el.updateElement(el.node);
30107                 }
30108                 table[r][c].cell = false; // done
30109             }
30110         }
30111     },
30112     normalizeWidths : function(table)
30113     {
30114         if (this.colWidths[0] === false) {
30115             var nw = 100.0 / this.colWidths.length;
30116             this.colWidths.forEach(function(w,i) {
30117                 this.colWidths[i] = nw;
30118             },this);
30119             return;
30120         }
30121     
30122         var t = 0, missing = [];
30123         
30124         this.colWidths.forEach(function(w,i) {
30125             //if you mix % and
30126             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30127             var add =  this.colWidths[i];
30128             if (add > 0) {
30129                 t+=add;
30130                 return;
30131             }
30132             missing.push(i);
30133             
30134             
30135         },this);
30136         var nc = this.colWidths.length;
30137         if (missing.length) {
30138             var mult = (nc - missing.length) / (1.0 * nc);
30139             var t = mult * t;
30140             var ew = (100 -t) / (1.0 * missing.length);
30141             this.colWidths.forEach(function(w,i) {
30142                 if (w > 0) {
30143                     this.colWidths[i] = w * mult;
30144                     return;
30145                 }
30146                 
30147                 this.colWidths[i] = ew;
30148             }, this);
30149             // have to make up numbers..
30150              
30151         }
30152         // now we should have all the widths..
30153         
30154     
30155     },
30156     
30157     shrinkColumn : function()
30158     {
30159         var table = this.toTableArray();
30160         this.normalizeWidths(table);
30161         var col = this.cellData.col;
30162         var nw = this.colWidths[col] * 0.8;
30163         if (nw < 5) {
30164             return;
30165         }
30166         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30167         this.colWidths.forEach(function(w,i) {
30168             if (i == col) {
30169                  this.colWidths[i] = nw;
30170                 return;
30171             }
30172             this.colWidths[i] += otherAdd
30173         }, this);
30174         this.updateWidths(table);
30175          
30176     },
30177     growColumn : function()
30178     {
30179         var table = this.toTableArray();
30180         this.normalizeWidths(table);
30181         var col = this.cellData.col;
30182         var nw = this.colWidths[col] * 1.2;
30183         if (nw > 90) {
30184             return;
30185         }
30186         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30187         this.colWidths.forEach(function(w,i) {
30188             if (i == col) {
30189                 this.colWidths[i] = nw;
30190                 return;
30191             }
30192             this.colWidths[i] -= otherSub
30193         }, this);
30194         this.updateWidths(table);
30195          
30196     },
30197     deleteRow : function()
30198     {
30199         // delete this rows 'tr'
30200         // if any of the cells in this row have a rowspan > 1 && row!= this row..
30201         // then reduce the rowspan.
30202         var table = this.toTableArray();
30203         // this.cellData.row;
30204         for (var i =0;i< table[this.cellData.row].length ; i++) {
30205             var c = table[this.cellData.row][i];
30206             if (c.row != this.cellData.row) {
30207                 
30208                 c.rowspan--;
30209                 c.cell.setAttribute('rowspan', c.rowspan);
30210                 continue;
30211             }
30212             if (c.rowspan > 1) {
30213                 c.rowspan--;
30214                 c.cell.setAttribute('rowspan', c.rowspan);
30215             }
30216         }
30217         table.splice(this.cellData.row,1);
30218         this.redrawAllCells(table);
30219         
30220     },
30221     deleteColumn : function()
30222     {
30223         var table = this.toTableArray();
30224         
30225         for (var i =0;i< table.length ; i++) {
30226             var c = table[i][this.cellData.col];
30227             if (c.col != this.cellData.col) {
30228                 table[i][this.cellData.col].colspan--;
30229             } else if (c.colspan > 1) {
30230                 c.colspan--;
30231                 c.cell.setAttribute('colspan', c.colspan);
30232             }
30233             table[i].splice(this.cellData.col,1);
30234         }
30235         
30236         this.redrawAllCells(table);
30237     }
30238     
30239     
30240     
30241     
30242 })
30243
30244 //<script type="text/javascript">
30245
30246 /*
30247  * Based  Ext JS Library 1.1.1
30248  * Copyright(c) 2006-2007, Ext JS, LLC.
30249  * LGPL
30250  *
30251  */
30252  
30253 /**
30254  * @class Roo.HtmlEditorCore
30255  * @extends Roo.Component
30256  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30257  *
30258  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30259  */
30260
30261 Roo.HtmlEditorCore = function(config){
30262     
30263     
30264     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30265     
30266     
30267     this.addEvents({
30268         /**
30269          * @event initialize
30270          * Fires when the editor is fully initialized (including the iframe)
30271          * @param {Roo.HtmlEditorCore} this
30272          */
30273         initialize: true,
30274         /**
30275          * @event activate
30276          * Fires when the editor is first receives the focus. Any insertion must wait
30277          * until after this event.
30278          * @param {Roo.HtmlEditorCore} this
30279          */
30280         activate: true,
30281          /**
30282          * @event beforesync
30283          * Fires before the textarea is updated with content from the editor iframe. Return false
30284          * to cancel the sync.
30285          * @param {Roo.HtmlEditorCore} this
30286          * @param {String} html
30287          */
30288         beforesync: true,
30289          /**
30290          * @event beforepush
30291          * Fires before the iframe editor is updated with content from the textarea. Return false
30292          * to cancel the push.
30293          * @param {Roo.HtmlEditorCore} this
30294          * @param {String} html
30295          */
30296         beforepush: true,
30297          /**
30298          * @event sync
30299          * Fires when the textarea is updated with content from the editor iframe.
30300          * @param {Roo.HtmlEditorCore} this
30301          * @param {String} html
30302          */
30303         sync: true,
30304          /**
30305          * @event push
30306          * Fires when the iframe editor is updated with content from the textarea.
30307          * @param {Roo.HtmlEditorCore} this
30308          * @param {String} html
30309          */
30310         push: true,
30311         
30312         /**
30313          * @event editorevent
30314          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30315          * @param {Roo.HtmlEditorCore} this
30316          */
30317         editorevent: true 
30318          
30319         
30320     });
30321     
30322     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30323     
30324     // defaults : white / black...
30325     this.applyBlacklists();
30326     
30327     
30328     
30329 };
30330
30331
30332 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
30333
30334
30335      /**
30336      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
30337      */
30338     
30339     owner : false,
30340     
30341      /**
30342      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
30343      *                        Roo.resizable.
30344      */
30345     resizable : false,
30346      /**
30347      * @cfg {Number} height (in pixels)
30348      */   
30349     height: 300,
30350    /**
30351      * @cfg {Number} width (in pixels)
30352      */   
30353     width: 500,
30354      /**
30355      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30356      *         if you are doing an email editor, this probably needs disabling, it's designed
30357      */
30358     autoClean: true,
30359     
30360     /**
30361      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30362      */
30363     enableBlocks : true,
30364     /**
30365      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30366      * 
30367      */
30368     stylesheets: false,
30369      /**
30370      * @cfg {String} language default en - language of text (usefull for rtl languages)
30371      * 
30372      */
30373     language: 'en',
30374     
30375     /**
30376      * @cfg {boolean} allowComments - default false - allow comments in HTML source
30377      *          - by default they are stripped - if you are editing email you may need this.
30378      */
30379     allowComments: false,
30380     // id of frame..
30381     frameId: false,
30382     
30383     // private properties
30384     validationEvent : false,
30385     deferHeight: true,
30386     initialized : false,
30387     activated : false,
30388     sourceEditMode : false,
30389     onFocus : Roo.emptyFn,
30390     iframePad:3,
30391     hideMode:'offsets',
30392     
30393     clearUp: true,
30394     
30395     // blacklist + whitelisted elements..
30396     black: false,
30397     white: false,
30398      
30399     bodyCls : '',
30400
30401     
30402     undoManager : false,
30403     /**
30404      * Protected method that will not generally be called directly. It
30405      * is called when the editor initializes the iframe with HTML contents. Override this method if you
30406      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30407      */
30408     getDocMarkup : function(){
30409         // body styles..
30410         var st = '';
30411         
30412         // inherit styels from page...?? 
30413         if (this.stylesheets === false) {
30414             
30415             Roo.get(document.head).select('style').each(function(node) {
30416                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30417             });
30418             
30419             Roo.get(document.head).select('link').each(function(node) { 
30420                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30421             });
30422             
30423         } else if (!this.stylesheets.length) {
30424                 // simple..
30425                 st = '<style type="text/css">' +
30426                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30427                    '</style>';
30428         } else {
30429             for (var i in this.stylesheets) {
30430                 if (typeof(this.stylesheets[i]) != 'string') {
30431                     continue;
30432                 }
30433                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30434             }
30435             
30436         }
30437         
30438         st +=  '<style type="text/css">' +
30439             'IMG { cursor: pointer } ' +
30440         '</style>';
30441         
30442         st += '<meta name="google" content="notranslate">';
30443         
30444         var cls = 'notranslate roo-htmleditor-body';
30445         
30446         if(this.bodyCls.length){
30447             cls += ' ' + this.bodyCls;
30448         }
30449         
30450         return '<html  class="notranslate" translate="no"><head>' + st  +
30451             //<style type="text/css">' +
30452             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30453             //'</style>' +
30454             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
30455     },
30456
30457     // private
30458     onRender : function(ct, position)
30459     {
30460         var _t = this;
30461         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
30462         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
30463         
30464         
30465         this.el.dom.style.border = '0 none';
30466         this.el.dom.setAttribute('tabIndex', -1);
30467         this.el.addClass('x-hidden hide');
30468         
30469         
30470         
30471         if(Roo.isIE){ // fix IE 1px bogus margin
30472             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
30473         }
30474        
30475         
30476         this.frameId = Roo.id();
30477         
30478          
30479         
30480         var iframe = this.owner.wrap.createChild({
30481             tag: 'iframe',
30482             cls: 'form-control', // bootstrap..
30483             id: this.frameId,
30484             name: this.frameId,
30485             frameBorder : 'no',
30486             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
30487         }, this.el
30488         );
30489         
30490         
30491         this.iframe = iframe.dom;
30492
30493         this.assignDocWin();
30494         
30495         this.doc.designMode = 'on';
30496        
30497         this.doc.open();
30498         this.doc.write(this.getDocMarkup());
30499         this.doc.close();
30500
30501         
30502         var task = { // must defer to wait for browser to be ready
30503             run : function(){
30504                 //console.log("run task?" + this.doc.readyState);
30505                 this.assignDocWin();
30506                 if(this.doc.body || this.doc.readyState == 'complete'){
30507                     try {
30508                         this.doc.designMode="on";
30509                         
30510                     } catch (e) {
30511                         return;
30512                     }
30513                     Roo.TaskMgr.stop(task);
30514                     this.initEditor.defer(10, this);
30515                 }
30516             },
30517             interval : 10,
30518             duration: 10000,
30519             scope: this
30520         };
30521         Roo.TaskMgr.start(task);
30522
30523     },
30524
30525     // private
30526     onResize : function(w, h)
30527     {
30528          Roo.log('resize: ' +w + ',' + h );
30529         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
30530         if(!this.iframe){
30531             return;
30532         }
30533         if(typeof w == 'number'){
30534             
30535             this.iframe.style.width = w + 'px';
30536         }
30537         if(typeof h == 'number'){
30538             
30539             this.iframe.style.height = h + 'px';
30540             if(this.doc){
30541                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
30542             }
30543         }
30544         
30545     },
30546
30547     /**
30548      * Toggles the editor between standard and source edit mode.
30549      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
30550      */
30551     toggleSourceEdit : function(sourceEditMode){
30552         
30553         this.sourceEditMode = sourceEditMode === true;
30554         
30555         if(this.sourceEditMode){
30556  
30557             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
30558             
30559         }else{
30560             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
30561             //this.iframe.className = '';
30562             this.deferFocus();
30563         }
30564         //this.setSize(this.owner.wrap.getSize());
30565         //this.fireEvent('editmodechange', this, this.sourceEditMode);
30566     },
30567
30568     
30569   
30570
30571     /**
30572      * Protected method that will not generally be called directly. If you need/want
30573      * custom HTML cleanup, this is the method you should override.
30574      * @param {String} html The HTML to be cleaned
30575      * return {String} The cleaned HTML
30576      */
30577     cleanHtml : function(html)
30578     {
30579         html = String(html);
30580         if(html.length > 5){
30581             if(Roo.isSafari){ // strip safari nonsense
30582                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
30583             }
30584         }
30585         if(html == '&nbsp;'){
30586             html = '';
30587         }
30588         return html;
30589     },
30590
30591     /**
30592      * HTML Editor -> Textarea
30593      * Protected method that will not generally be called directly. Syncs the contents
30594      * of the editor iframe with the textarea.
30595      */
30596     syncValue : function()
30597     {
30598         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
30599         if(this.initialized){
30600             
30601             if (this.undoManager) {
30602                 this.undoManager.addEvent();
30603             }
30604
30605             
30606             var bd = (this.doc.body || this.doc.documentElement);
30607            
30608             
30609             var sel = this.win.getSelection();
30610             
30611             var div = document.createElement('div');
30612             div.innerHTML = bd.innerHTML;
30613             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
30614             if (gtx.length > 0) {
30615                 var rm = gtx.item(0).parentNode;
30616                 rm.parentNode.removeChild(rm);
30617             }
30618             
30619            
30620             if (this.enableBlocks) {
30621                 new Roo.htmleditor.FilterBlock({ node : div });
30622             }
30623             
30624             var html = div.innerHTML;
30625             
30626             //?? tidy?
30627             if (this.autoClean) {
30628                 
30629                 new Roo.htmleditor.FilterAttributes({
30630                     node : div,
30631                     attrib_white : [
30632                             'href',
30633                             'src',
30634                             'name',
30635                             'align',
30636                             'colspan',
30637                             'rowspan',
30638                             'data-display',
30639                             'data-width',
30640                             'start' ,
30641                             'style',
30642                             // youtube embed.
30643                             'class',
30644                             'allowfullscreen',
30645                             'frameborder',
30646                             'width',
30647                             'height',
30648                             'alt'
30649                             ],
30650                     attrib_clean : ['href', 'src' ] 
30651                 });
30652                 
30653                 var tidy = new Roo.htmleditor.TidySerializer({
30654                     inner:  true
30655                 });
30656                 html  = tidy.serialize(div);
30657                 
30658             }
30659             
30660             
30661             if(Roo.isSafari){
30662                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
30663                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
30664                 if(m && m[1]){
30665                     html = '<div style="'+m[0]+'">' + html + '</div>';
30666                 }
30667             }
30668             html = this.cleanHtml(html);
30669             // fix up the special chars.. normaly like back quotes in word...
30670             // however we do not want to do this with chinese..
30671             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
30672                 
30673                 var cc = match.charCodeAt();
30674
30675                 // Get the character value, handling surrogate pairs
30676                 if (match.length == 2) {
30677                     // It's a surrogate pair, calculate the Unicode code point
30678                     var high = match.charCodeAt(0) - 0xD800;
30679                     var low  = match.charCodeAt(1) - 0xDC00;
30680                     cc = (high * 0x400) + low + 0x10000;
30681                 }  else if (
30682                     (cc >= 0x4E00 && cc < 0xA000 ) ||
30683                     (cc >= 0x3400 && cc < 0x4E00 ) ||
30684                     (cc >= 0xf900 && cc < 0xfb00 )
30685                 ) {
30686                         return match;
30687                 }  
30688          
30689                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
30690                 return "&#" + cc + ";";
30691                 
30692                 
30693             });
30694             
30695             
30696              
30697             if(this.owner.fireEvent('beforesync', this, html) !== false){
30698                 this.el.dom.value = html;
30699                 this.owner.fireEvent('sync', this, html);
30700             }
30701         }
30702     },
30703
30704     /**
30705      * TEXTAREA -> EDITABLE
30706      * Protected method that will not generally be called directly. Pushes the value of the textarea
30707      * into the iframe editor.
30708      */
30709     pushValue : function()
30710     {
30711         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
30712         if(this.initialized){
30713             var v = this.el.dom.value.trim();
30714             
30715             
30716             if(this.owner.fireEvent('beforepush', this, v) !== false){
30717                 var d = (this.doc.body || this.doc.documentElement);
30718                 d.innerHTML = v;
30719                  
30720                 this.el.dom.value = d.innerHTML;
30721                 this.owner.fireEvent('push', this, v);
30722             }
30723             if (this.autoClean) {
30724                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
30725                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
30726             }
30727             if (this.enableBlocks) {
30728                 Roo.htmleditor.Block.initAll(this.doc.body);
30729             }
30730             
30731             this.updateLanguage();
30732             
30733             var lc = this.doc.body.lastChild;
30734             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
30735                 // add an extra line at the end.
30736                 this.doc.body.appendChild(this.doc.createElement('br'));
30737             }
30738             
30739             
30740         }
30741     },
30742
30743     // private
30744     deferFocus : function(){
30745         this.focus.defer(10, this);
30746     },
30747
30748     // doc'ed in Field
30749     focus : function(){
30750         if(this.win && !this.sourceEditMode){
30751             this.win.focus();
30752         }else{
30753             this.el.focus();
30754         }
30755     },
30756     
30757     assignDocWin: function()
30758     {
30759         var iframe = this.iframe;
30760         
30761          if(Roo.isIE){
30762             this.doc = iframe.contentWindow.document;
30763             this.win = iframe.contentWindow;
30764         } else {
30765 //            if (!Roo.get(this.frameId)) {
30766 //                return;
30767 //            }
30768 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
30769 //            this.win = Roo.get(this.frameId).dom.contentWindow;
30770             
30771             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
30772                 return;
30773             }
30774             
30775             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
30776             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
30777         }
30778     },
30779     
30780     // private
30781     initEditor : function(){
30782         //console.log("INIT EDITOR");
30783         this.assignDocWin();
30784         
30785         
30786         
30787         this.doc.designMode="on";
30788         this.doc.open();
30789         this.doc.write(this.getDocMarkup());
30790         this.doc.close();
30791         
30792         var dbody = (this.doc.body || this.doc.documentElement);
30793         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
30794         // this copies styles from the containing element into thsi one..
30795         // not sure why we need all of this..
30796         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
30797         
30798         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
30799         //ss['background-attachment'] = 'fixed'; // w3c
30800         dbody.bgProperties = 'fixed'; // ie
30801         dbody.setAttribute("translate", "no");
30802         
30803         //Roo.DomHelper.applyStyles(dbody, ss);
30804         Roo.EventManager.on(this.doc, {
30805              
30806             'mouseup': this.onEditorEvent,
30807             'dblclick': this.onEditorEvent,
30808             'click': this.onEditorEvent,
30809             'keyup': this.onEditorEvent,
30810             
30811             buffer:100,
30812             scope: this
30813         });
30814         Roo.EventManager.on(this.doc, {
30815             'paste': this.onPasteEvent,
30816             scope : this
30817         });
30818         if(Roo.isGecko){
30819             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
30820         }
30821         //??? needed???
30822         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
30823             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
30824         }
30825         this.initialized = true;
30826
30827         
30828         // initialize special key events - enter
30829         new Roo.htmleditor.KeyEnter({core : this});
30830         
30831          
30832         
30833         this.owner.fireEvent('initialize', this);
30834         this.pushValue();
30835     },
30836     // this is to prevent a href clicks resulting in a redirect?
30837    
30838     onPasteEvent : function(e,v)
30839     {
30840         // I think we better assume paste is going to be a dirty load of rubish from word..
30841         
30842         // even pasting into a 'email version' of this widget will have to clean up that mess.
30843         var cd = (e.browserEvent.clipboardData || window.clipboardData);
30844         
30845         // check what type of paste - if it's an image, then handle it differently.
30846         if (cd.files && cd.files.length > 0) {
30847             // pasting images?
30848             var urlAPI = (window.createObjectURL && window) || 
30849                 (window.URL && URL.revokeObjectURL && URL) || 
30850                 (window.webkitURL && webkitURL);
30851     
30852             var url = urlAPI.createObjectURL( cd.files[0]);
30853             this.insertAtCursor('<img src=" + url + ">');
30854             return false;
30855         }
30856         if (cd.types.indexOf('text/html') < 0 ) {
30857             return false;
30858         }
30859         var images = [];
30860         var html = cd.getData('text/html'); // clipboard event
30861         if (cd.types.indexOf('text/rtf') > -1) {
30862             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
30863             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
30864         }
30865         //Roo.log(images);
30866         //Roo.log(imgs);
30867         // fixme..
30868         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
30869                        .map(function(g) { return g.toDataURL(); })
30870                        .filter(function(g) { return g != 'about:blank'; });
30871         
30872         //Roo.log(html);
30873         html = this.cleanWordChars(html);
30874         
30875         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
30876         
30877         
30878         var sn = this.getParentElement();
30879         // check if d contains a table, and prevent nesting??
30880         //Roo.log(d.getElementsByTagName('table'));
30881         //Roo.log(sn);
30882         //Roo.log(sn.closest('table'));
30883         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
30884             e.preventDefault();
30885             this.insertAtCursor("You can not nest tables");
30886             //Roo.log("prevent?"); // fixme - 
30887             return false;
30888         }
30889         
30890         
30891         
30892         if (images.length > 0) {
30893             // replace all v:imagedata - with img.
30894             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
30895             Roo.each(ar, function(node) {
30896                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
30897                 node.parentNode.removeChild(node);
30898             });
30899             
30900             
30901             Roo.each(d.getElementsByTagName('img'), function(img, i) {
30902                 img.setAttribute('src', images[i]);
30903             });
30904         }
30905         if (this.autoClean) {
30906             new Roo.htmleditor.FilterWord({ node : d });
30907             
30908             new Roo.htmleditor.FilterStyleToTag({ node : d });
30909             new Roo.htmleditor.FilterAttributes({
30910                 node : d,
30911                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
30912                 attrib_clean : ['href', 'src' ] 
30913             });
30914             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
30915             // should be fonts..
30916             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
30917             new Roo.htmleditor.FilterParagraph({ node : d });
30918             new Roo.htmleditor.FilterSpan({ node : d });
30919             new Roo.htmleditor.FilterLongBr({ node : d });
30920             new Roo.htmleditor.FilterComment({ node : d });
30921             
30922             
30923         }
30924         if (this.enableBlocks) {
30925                 
30926             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
30927                 if (img.closest('figure')) { // assume!! that it's aready
30928                     return;
30929                 }
30930                 var fig  = new Roo.htmleditor.BlockFigure({
30931                     image_src  : img.src
30932                 });
30933                 fig.updateElement(img); // replace it..
30934                 
30935             });
30936         }
30937         
30938         
30939         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
30940         if (this.enableBlocks) {
30941             Roo.htmleditor.Block.initAll(this.doc.body);
30942         }
30943          
30944         
30945         e.preventDefault();
30946         return false;
30947         // default behaveiour should be our local cleanup paste? (optional?)
30948         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
30949         //this.owner.fireEvent('paste', e, v);
30950     },
30951     // private
30952     onDestroy : function(){
30953         
30954         
30955         
30956         if(this.rendered){
30957             
30958             //for (var i =0; i < this.toolbars.length;i++) {
30959             //    // fixme - ask toolbars for heights?
30960             //    this.toolbars[i].onDestroy();
30961            // }
30962             
30963             //this.wrap.dom.innerHTML = '';
30964             //this.wrap.remove();
30965         }
30966     },
30967
30968     // private
30969     onFirstFocus : function(){
30970         
30971         this.assignDocWin();
30972         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
30973         
30974         this.activated = true;
30975          
30976     
30977         if(Roo.isGecko){ // prevent silly gecko errors
30978             this.win.focus();
30979             var s = this.win.getSelection();
30980             if(!s.focusNode || s.focusNode.nodeType != 3){
30981                 var r = s.getRangeAt(0);
30982                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
30983                 r.collapse(true);
30984                 this.deferFocus();
30985             }
30986             try{
30987                 this.execCmd('useCSS', true);
30988                 this.execCmd('styleWithCSS', false);
30989             }catch(e){}
30990         }
30991         this.owner.fireEvent('activate', this);
30992     },
30993
30994     // private
30995     adjustFont: function(btn){
30996         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
30997         //if(Roo.isSafari){ // safari
30998         //    adjust *= 2;
30999        // }
31000         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31001         if(Roo.isSafari){ // safari
31002             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31003             v =  (v < 10) ? 10 : v;
31004             v =  (v > 48) ? 48 : v;
31005             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31006             
31007         }
31008         
31009         
31010         v = Math.max(1, v+adjust);
31011         
31012         this.execCmd('FontSize', v  );
31013     },
31014
31015     onEditorEvent : function(e)
31016     {
31017          
31018         
31019         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31020             return; // we do not handle this.. (undo manager does..)
31021         }
31022         // in theory this detects if the last element is not a br, then we try and do that.
31023         // its so clicking in space at bottom triggers adding a br and moving the cursor.
31024         if (e &&
31025             e.target.nodeName == 'BODY' &&
31026             e.type == "mouseup" &&
31027             this.doc.body.lastChild
31028            ) {
31029             var lc = this.doc.body.lastChild;
31030             // gtx-trans is google translate plugin adding crap.
31031             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31032                 lc = lc.previousSibling;
31033             }
31034             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31035             // if last element is <BR> - then dont do anything.
31036             
31037                 var ns = this.doc.createElement('br');
31038                 this.doc.body.appendChild(ns);
31039                 range = this.doc.createRange();
31040                 range.setStartAfter(ns);
31041                 range.collapse(true);
31042                 var sel = this.win.getSelection();
31043                 sel.removeAllRanges();
31044                 sel.addRange(range);
31045             }
31046         }
31047         
31048         
31049         
31050         this.fireEditorEvent(e);
31051       //  this.updateToolbar();
31052         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31053     },
31054     
31055     fireEditorEvent: function(e)
31056     {
31057         this.owner.fireEvent('editorevent', this, e);
31058     },
31059
31060     insertTag : function(tg)
31061     {
31062         // could be a bit smarter... -> wrap the current selected tRoo..
31063         if (tg.toLowerCase() == 'span' ||
31064             tg.toLowerCase() == 'code' ||
31065             tg.toLowerCase() == 'sup' ||
31066             tg.toLowerCase() == 'sub' 
31067             ) {
31068             
31069             range = this.createRange(this.getSelection());
31070             var wrappingNode = this.doc.createElement(tg.toLowerCase());
31071             wrappingNode.appendChild(range.extractContents());
31072             range.insertNode(wrappingNode);
31073
31074             return;
31075             
31076             
31077             
31078         }
31079         this.execCmd("formatblock",   tg);
31080         this.undoManager.addEvent(); 
31081     },
31082     
31083     insertText : function(txt)
31084     {
31085         
31086         
31087         var range = this.createRange();
31088         range.deleteContents();
31089                //alert(Sender.getAttribute('label'));
31090                
31091         range.insertNode(this.doc.createTextNode(txt));
31092         this.undoManager.addEvent();
31093     } ,
31094     
31095      
31096
31097     /**
31098      * Executes a Midas editor command on the editor document and performs necessary focus and
31099      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31100      * @param {String} cmd The Midas command
31101      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31102      */
31103     relayCmd : function(cmd, value)
31104     {
31105         
31106         switch (cmd) {
31107             case 'justifyleft':
31108             case 'justifyright':
31109             case 'justifycenter':
31110                 // if we are in a cell, then we will adjust the
31111                 var n = this.getParentElement();
31112                 var td = n.closest('td');
31113                 if (td) {
31114                     var bl = Roo.htmleditor.Block.factory(td);
31115                     bl.textAlign = cmd.replace('justify','');
31116                     bl.updateElement();
31117                     this.owner.fireEvent('editorevent', this);
31118                     return;
31119                 }
31120                 this.execCmd('styleWithCSS', true); // 
31121                 break;
31122             case 'bold':
31123             case 'italic':
31124                 // if there is no selection, then we insert, and set the curson inside it..
31125                 this.execCmd('styleWithCSS', false); 
31126                 break;
31127                 
31128         
31129             default:
31130                 break;
31131         }
31132         
31133         
31134         this.win.focus();
31135         this.execCmd(cmd, value);
31136         this.owner.fireEvent('editorevent', this);
31137         //this.updateToolbar();
31138         this.owner.deferFocus();
31139     },
31140
31141     /**
31142      * Executes a Midas editor command directly on the editor document.
31143      * For visual commands, you should use {@link #relayCmd} instead.
31144      * <b>This should only be called after the editor is initialized.</b>
31145      * @param {String} cmd The Midas command
31146      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31147      */
31148     execCmd : function(cmd, value){
31149         this.doc.execCommand(cmd, false, value === undefined ? null : value);
31150         this.syncValue();
31151     },
31152  
31153  
31154    
31155     /**
31156      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31157      * to insert tRoo.
31158      * @param {String} text | dom node.. 
31159      */
31160     insertAtCursor : function(text)
31161     {
31162         
31163         if(!this.activated){
31164             return;
31165         }
31166          
31167         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31168             this.win.focus();
31169             
31170             
31171             // from jquery ui (MIT licenced)
31172             var range, node;
31173             var win = this.win;
31174             
31175             if (win.getSelection && win.getSelection().getRangeAt) {
31176                 
31177                 // delete the existing?
31178                 
31179                 this.createRange(this.getSelection()).deleteContents();
31180                 range = win.getSelection().getRangeAt(0);
31181                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31182                 range.insertNode(node);
31183                 range = range.cloneRange();
31184                 range.collapse(false);
31185                  
31186                 win.getSelection().removeAllRanges();
31187                 win.getSelection().addRange(range);
31188                 
31189                 
31190                 
31191             } else if (win.document.selection && win.document.selection.createRange) {
31192                 // no firefox support
31193                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31194                 win.document.selection.createRange().pasteHTML(txt);
31195             
31196             } else {
31197                 // no firefox support
31198                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31199                 this.execCmd('InsertHTML', txt);
31200             } 
31201             this.syncValue();
31202             
31203             this.deferFocus();
31204         }
31205     },
31206  // private
31207     mozKeyPress : function(e){
31208         if(e.ctrlKey){
31209             var c = e.getCharCode(), cmd;
31210           
31211             if(c > 0){
31212                 c = String.fromCharCode(c).toLowerCase();
31213                 switch(c){
31214                     case 'b':
31215                         cmd = 'bold';
31216                         break;
31217                     case 'i':
31218                         cmd = 'italic';
31219                         break;
31220                     
31221                     case 'u':
31222                         cmd = 'underline';
31223                         break;
31224                     
31225                     //case 'v':
31226                       //  this.cleanUpPaste.defer(100, this);
31227                       //  return;
31228                         
31229                 }
31230                 if(cmd){
31231                     
31232                     this.relayCmd(cmd);
31233                     //this.win.focus();
31234                     //this.execCmd(cmd);
31235                     //this.deferFocus();
31236                     e.preventDefault();
31237                 }
31238                 
31239             }
31240         }
31241     },
31242
31243     // private
31244     fixKeys : function(){ // load time branching for fastest keydown performance
31245         
31246         
31247         if(Roo.isIE){
31248             return function(e){
31249                 var k = e.getKey(), r;
31250                 if(k == e.TAB){
31251                     e.stopEvent();
31252                     r = this.doc.selection.createRange();
31253                     if(r){
31254                         r.collapse(true);
31255                         r.pasteHTML('&#160;&#160;&#160;&#160;');
31256                         this.deferFocus();
31257                     }
31258                     return;
31259                 }
31260                 /// this is handled by Roo.htmleditor.KeyEnter
31261                  /*
31262                 if(k == e.ENTER){
31263                     r = this.doc.selection.createRange();
31264                     if(r){
31265                         var target = r.parentElement();
31266                         if(!target || target.tagName.toLowerCase() != 'li'){
31267                             e.stopEvent();
31268                             r.pasteHTML('<br/>');
31269                             r.collapse(false);
31270                             r.select();
31271                         }
31272                     }
31273                 }
31274                 */
31275                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31276                 //    this.cleanUpPaste.defer(100, this);
31277                 //    return;
31278                 //}
31279                 
31280                 
31281             };
31282         }else if(Roo.isOpera){
31283             return function(e){
31284                 var k = e.getKey();
31285                 if(k == e.TAB){
31286                     e.stopEvent();
31287                     this.win.focus();
31288                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
31289                     this.deferFocus();
31290                 }
31291                
31292                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31293                 //    this.cleanUpPaste.defer(100, this);
31294                  //   return;
31295                 //}
31296                 
31297             };
31298         }else if(Roo.isSafari){
31299             return function(e){
31300                 var k = e.getKey();
31301                 
31302                 if(k == e.TAB){
31303                     e.stopEvent();
31304                     this.execCmd('InsertText','\t');
31305                     this.deferFocus();
31306                     return;
31307                 }
31308                  this.mozKeyPress(e);
31309                 
31310                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31311                  //   this.cleanUpPaste.defer(100, this);
31312                  //   return;
31313                // }
31314                 
31315              };
31316         }
31317     }(),
31318     
31319     getAllAncestors: function()
31320     {
31321         var p = this.getSelectedNode();
31322         var a = [];
31323         if (!p) {
31324             a.push(p); // push blank onto stack..
31325             p = this.getParentElement();
31326         }
31327         
31328         
31329         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31330             a.push(p);
31331             p = p.parentNode;
31332         }
31333         a.push(this.doc.body);
31334         return a;
31335     },
31336     lastSel : false,
31337     lastSelNode : false,
31338     
31339     
31340     getSelection : function() 
31341     {
31342         this.assignDocWin();
31343         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31344     },
31345     /**
31346      * Select a dom node
31347      * @param {DomElement} node the node to select
31348      */
31349     selectNode : function(node, collapse)
31350     {
31351         var nodeRange = node.ownerDocument.createRange();
31352         try {
31353             nodeRange.selectNode(node);
31354         } catch (e) {
31355             nodeRange.selectNodeContents(node);
31356         }
31357         if (collapse === true) {
31358             nodeRange.collapse(true);
31359         }
31360         //
31361         var s = this.win.getSelection();
31362         s.removeAllRanges();
31363         s.addRange(nodeRange);
31364     },
31365     
31366     getSelectedNode: function() 
31367     {
31368         // this may only work on Gecko!!!
31369         
31370         // should we cache this!!!!
31371         
31372          
31373          
31374         var range = this.createRange(this.getSelection()).cloneRange();
31375         
31376         if (Roo.isIE) {
31377             var parent = range.parentElement();
31378             while (true) {
31379                 var testRange = range.duplicate();
31380                 testRange.moveToElementText(parent);
31381                 if (testRange.inRange(range)) {
31382                     break;
31383                 }
31384                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31385                     break;
31386                 }
31387                 parent = parent.parentElement;
31388             }
31389             return parent;
31390         }
31391         
31392         // is ancestor a text element.
31393         var ac =  range.commonAncestorContainer;
31394         if (ac.nodeType == 3) {
31395             ac = ac.parentNode;
31396         }
31397         
31398         var ar = ac.childNodes;
31399          
31400         var nodes = [];
31401         var other_nodes = [];
31402         var has_other_nodes = false;
31403         for (var i=0;i<ar.length;i++) {
31404             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
31405                 continue;
31406             }
31407             // fullly contained node.
31408             
31409             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31410                 nodes.push(ar[i]);
31411                 continue;
31412             }
31413             
31414             // probably selected..
31415             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31416                 other_nodes.push(ar[i]);
31417                 continue;
31418             }
31419             // outer..
31420             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
31421                 continue;
31422             }
31423             
31424             
31425             has_other_nodes = true;
31426         }
31427         if (!nodes.length && other_nodes.length) {
31428             nodes= other_nodes;
31429         }
31430         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
31431             return false;
31432         }
31433         
31434         return nodes[0];
31435     },
31436     
31437     
31438     createRange: function(sel)
31439     {
31440         // this has strange effects when using with 
31441         // top toolbar - not sure if it's a great idea.
31442         //this.editor.contentWindow.focus();
31443         if (typeof sel != "undefined") {
31444             try {
31445                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
31446             } catch(e) {
31447                 return this.doc.createRange();
31448             }
31449         } else {
31450             return this.doc.createRange();
31451         }
31452     },
31453     getParentElement: function()
31454     {
31455         
31456         this.assignDocWin();
31457         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
31458         
31459         var range = this.createRange(sel);
31460          
31461         try {
31462             var p = range.commonAncestorContainer;
31463             while (p.nodeType == 3) { // text node
31464                 p = p.parentNode;
31465             }
31466             return p;
31467         } catch (e) {
31468             return null;
31469         }
31470     
31471     },
31472     /***
31473      *
31474      * Range intersection.. the hard stuff...
31475      *  '-1' = before
31476      *  '0' = hits..
31477      *  '1' = after.
31478      *         [ -- selected range --- ]
31479      *   [fail]                        [fail]
31480      *
31481      *    basically..
31482      *      if end is before start or  hits it. fail.
31483      *      if start is after end or hits it fail.
31484      *
31485      *   if either hits (but other is outside. - then it's not 
31486      *   
31487      *    
31488      **/
31489     
31490     
31491     // @see http://www.thismuchiknow.co.uk/?p=64.
31492     rangeIntersectsNode : function(range, node)
31493     {
31494         var nodeRange = node.ownerDocument.createRange();
31495         try {
31496             nodeRange.selectNode(node);
31497         } catch (e) {
31498             nodeRange.selectNodeContents(node);
31499         }
31500     
31501         var rangeStartRange = range.cloneRange();
31502         rangeStartRange.collapse(true);
31503     
31504         var rangeEndRange = range.cloneRange();
31505         rangeEndRange.collapse(false);
31506     
31507         var nodeStartRange = nodeRange.cloneRange();
31508         nodeStartRange.collapse(true);
31509     
31510         var nodeEndRange = nodeRange.cloneRange();
31511         nodeEndRange.collapse(false);
31512     
31513         return rangeStartRange.compareBoundaryPoints(
31514                  Range.START_TO_START, nodeEndRange) == -1 &&
31515                rangeEndRange.compareBoundaryPoints(
31516                  Range.START_TO_START, nodeStartRange) == 1;
31517         
31518          
31519     },
31520     rangeCompareNode : function(range, node)
31521     {
31522         var nodeRange = node.ownerDocument.createRange();
31523         try {
31524             nodeRange.selectNode(node);
31525         } catch (e) {
31526             nodeRange.selectNodeContents(node);
31527         }
31528         
31529         
31530         range.collapse(true);
31531     
31532         nodeRange.collapse(true);
31533      
31534         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
31535         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
31536          
31537         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
31538         
31539         var nodeIsBefore   =  ss == 1;
31540         var nodeIsAfter    = ee == -1;
31541         
31542         if (nodeIsBefore && nodeIsAfter) {
31543             return 0; // outer
31544         }
31545         if (!nodeIsBefore && nodeIsAfter) {
31546             return 1; //right trailed.
31547         }
31548         
31549         if (nodeIsBefore && !nodeIsAfter) {
31550             return 2;  // left trailed.
31551         }
31552         // fully contined.
31553         return 3;
31554     },
31555  
31556     cleanWordChars : function(input) {// change the chars to hex code
31557         
31558        var swapCodes  = [ 
31559             [    8211, "&#8211;" ], 
31560             [    8212, "&#8212;" ], 
31561             [    8216,  "'" ],  
31562             [    8217, "'" ],  
31563             [    8220, '"' ],  
31564             [    8221, '"' ],  
31565             [    8226, "*" ],  
31566             [    8230, "..." ]
31567         ]; 
31568         var output = input;
31569         Roo.each(swapCodes, function(sw) { 
31570             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
31571             
31572             output = output.replace(swapper, sw[1]);
31573         });
31574         
31575         return output;
31576     },
31577     
31578      
31579     
31580         
31581     
31582     cleanUpChild : function (node)
31583     {
31584         
31585         new Roo.htmleditor.FilterComment({node : node});
31586         new Roo.htmleditor.FilterAttributes({
31587                 node : node,
31588                 attrib_black : this.ablack,
31589                 attrib_clean : this.aclean,
31590                 style_white : this.cwhite,
31591                 style_black : this.cblack
31592         });
31593         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
31594         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
31595          
31596         
31597     },
31598     
31599     /**
31600      * Clean up MS wordisms...
31601      * @deprecated - use filter directly
31602      */
31603     cleanWord : function(node)
31604     {
31605         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
31606         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
31607         
31608     },
31609    
31610     
31611     /**
31612
31613      * @deprecated - use filters
31614      */
31615     cleanTableWidths : function(node)
31616     {
31617         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
31618         
31619  
31620     },
31621     
31622      
31623         
31624     applyBlacklists : function()
31625     {
31626         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
31627         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
31628         
31629         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
31630         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
31631         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
31632         
31633         this.white = [];
31634         this.black = [];
31635         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
31636             if (b.indexOf(tag) > -1) {
31637                 return;
31638             }
31639             this.white.push(tag);
31640             
31641         }, this);
31642         
31643         Roo.each(w, function(tag) {
31644             if (b.indexOf(tag) > -1) {
31645                 return;
31646             }
31647             if (this.white.indexOf(tag) > -1) {
31648                 return;
31649             }
31650             this.white.push(tag);
31651             
31652         }, this);
31653         
31654         
31655         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
31656             if (w.indexOf(tag) > -1) {
31657                 return;
31658             }
31659             this.black.push(tag);
31660             
31661         }, this);
31662         
31663         Roo.each(b, function(tag) {
31664             if (w.indexOf(tag) > -1) {
31665                 return;
31666             }
31667             if (this.black.indexOf(tag) > -1) {
31668                 return;
31669             }
31670             this.black.push(tag);
31671             
31672         }, this);
31673         
31674         
31675         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
31676         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
31677         
31678         this.cwhite = [];
31679         this.cblack = [];
31680         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
31681             if (b.indexOf(tag) > -1) {
31682                 return;
31683             }
31684             this.cwhite.push(tag);
31685             
31686         }, this);
31687         
31688         Roo.each(w, function(tag) {
31689             if (b.indexOf(tag) > -1) {
31690                 return;
31691             }
31692             if (this.cwhite.indexOf(tag) > -1) {
31693                 return;
31694             }
31695             this.cwhite.push(tag);
31696             
31697         }, this);
31698         
31699         
31700         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
31701             if (w.indexOf(tag) > -1) {
31702                 return;
31703             }
31704             this.cblack.push(tag);
31705             
31706         }, this);
31707         
31708         Roo.each(b, function(tag) {
31709             if (w.indexOf(tag) > -1) {
31710                 return;
31711             }
31712             if (this.cblack.indexOf(tag) > -1) {
31713                 return;
31714             }
31715             this.cblack.push(tag);
31716             
31717         }, this);
31718     },
31719     
31720     setStylesheets : function(stylesheets)
31721     {
31722         if(typeof(stylesheets) == 'string'){
31723             Roo.get(this.iframe.contentDocument.head).createChild({
31724                 tag : 'link',
31725                 rel : 'stylesheet',
31726                 type : 'text/css',
31727                 href : stylesheets
31728             });
31729             
31730             return;
31731         }
31732         var _this = this;
31733      
31734         Roo.each(stylesheets, function(s) {
31735             if(!s.length){
31736                 return;
31737             }
31738             
31739             Roo.get(_this.iframe.contentDocument.head).createChild({
31740                 tag : 'link',
31741                 rel : 'stylesheet',
31742                 type : 'text/css',
31743                 href : s
31744             });
31745         });
31746
31747         
31748     },
31749     
31750     
31751     updateLanguage : function()
31752     {
31753         if (!this.iframe || !this.iframe.contentDocument) {
31754             return;
31755         }
31756         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
31757     },
31758     
31759     
31760     removeStylesheets : function()
31761     {
31762         var _this = this;
31763         
31764         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
31765             s.remove();
31766         });
31767     },
31768     
31769     setStyle : function(style)
31770     {
31771         Roo.get(this.iframe.contentDocument.head).createChild({
31772             tag : 'style',
31773             type : 'text/css',
31774             html : style
31775         });
31776
31777         return;
31778     }
31779     
31780     // hide stuff that is not compatible
31781     /**
31782      * @event blur
31783      * @hide
31784      */
31785     /**
31786      * @event change
31787      * @hide
31788      */
31789     /**
31790      * @event focus
31791      * @hide
31792      */
31793     /**
31794      * @event specialkey
31795      * @hide
31796      */
31797     /**
31798      * @cfg {String} fieldClass @hide
31799      */
31800     /**
31801      * @cfg {String} focusClass @hide
31802      */
31803     /**
31804      * @cfg {String} autoCreate @hide
31805      */
31806     /**
31807      * @cfg {String} inputType @hide
31808      */
31809     /**
31810      * @cfg {String} invalidClass @hide
31811      */
31812     /**
31813      * @cfg {String} invalidText @hide
31814      */
31815     /**
31816      * @cfg {String} msgFx @hide
31817      */
31818     /**
31819      * @cfg {String} validateOnBlur @hide
31820      */
31821 });
31822
31823 Roo.HtmlEditorCore.white = [
31824         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
31825         
31826        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
31827        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
31828        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
31829        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
31830        'TABLE',   'UL',         'XMP', 
31831        
31832        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
31833       'THEAD',   'TR', 
31834      
31835       'DIR', 'MENU', 'OL', 'UL', 'DL',
31836        
31837       'EMBED',  'OBJECT'
31838 ];
31839
31840
31841 Roo.HtmlEditorCore.black = [
31842     //    'embed',  'object', // enable - backend responsiblity to clean thiese
31843         'APPLET', // 
31844         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
31845         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
31846         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
31847         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
31848         //'FONT' // CLEAN LATER..
31849         'COLGROUP', 'COL'   // messy tables.
31850         
31851         
31852 ];
31853 Roo.HtmlEditorCore.clean = [ // ?? needed???
31854      'SCRIPT', 'STYLE', 'TITLE', 'XML'
31855 ];
31856 Roo.HtmlEditorCore.tag_remove = [
31857     'FONT', 'TBODY'  
31858 ];
31859 // attributes..
31860
31861 Roo.HtmlEditorCore.ablack = [
31862     'on'
31863 ];
31864     
31865 Roo.HtmlEditorCore.aclean = [ 
31866     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
31867 ];
31868
31869 // protocols..
31870 Roo.HtmlEditorCore.pwhite= [
31871         'http',  'https',  'mailto'
31872 ];
31873
31874 // white listed style attributes.
31875 Roo.HtmlEditorCore.cwhite= [
31876       //  'text-align', /// default is to allow most things..
31877       
31878          
31879 //        'font-size'//??
31880 ];
31881
31882 // black listed style attributes.
31883 Roo.HtmlEditorCore.cblack= [
31884       //  'font-size' -- this can be set by the project 
31885 ];
31886
31887
31888
31889
31890     /*
31891  * - LGPL
31892  *
31893  * HtmlEditor
31894  * 
31895  */
31896
31897 /**
31898  * @class Roo.bootstrap.form.HtmlEditor
31899  * @extends Roo.bootstrap.form.TextArea
31900  * Bootstrap HtmlEditor class
31901
31902  * @constructor
31903  * Create a new HtmlEditor
31904  * @param {Object} config The config object
31905  */
31906
31907 Roo.bootstrap.form.HtmlEditor = function(config){
31908     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
31909     if (!this.toolbars) {
31910         this.toolbars = [];
31911     }
31912     
31913     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
31914     this.addEvents({
31915             /**
31916              * @event initialize
31917              * Fires when the editor is fully initialized (including the iframe)
31918              * @param {HtmlEditor} this
31919              */
31920             initialize: true,
31921             /**
31922              * @event activate
31923              * Fires when the editor is first receives the focus. Any insertion must wait
31924              * until after this event.
31925              * @param {HtmlEditor} this
31926              */
31927             activate: true,
31928              /**
31929              * @event beforesync
31930              * Fires before the textarea is updated with content from the editor iframe. Return false
31931              * to cancel the sync.
31932              * @param {HtmlEditor} this
31933              * @param {String} html
31934              */
31935             beforesync: true,
31936              /**
31937              * @event beforepush
31938              * Fires before the iframe editor is updated with content from the textarea. Return false
31939              * to cancel the push.
31940              * @param {HtmlEditor} this
31941              * @param {String} html
31942              */
31943             beforepush: true,
31944              /**
31945              * @event sync
31946              * Fires when the textarea is updated with content from the editor iframe.
31947              * @param {HtmlEditor} this
31948              * @param {String} html
31949              */
31950             sync: true,
31951              /**
31952              * @event push
31953              * Fires when the iframe editor is updated with content from the textarea.
31954              * @param {HtmlEditor} this
31955              * @param {String} html
31956              */
31957             push: true,
31958              /**
31959              * @event editmodechange
31960              * Fires when the editor switches edit modes
31961              * @param {HtmlEditor} this
31962              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
31963              */
31964             editmodechange: true,
31965             /**
31966              * @event editorevent
31967              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
31968              * @param {HtmlEditor} this
31969              */
31970             editorevent: true,
31971             /**
31972              * @event firstfocus
31973              * Fires when on first focus - needed by toolbars..
31974              * @param {HtmlEditor} this
31975              */
31976             firstfocus: true,
31977             /**
31978              * @event autosave
31979              * Auto save the htmlEditor value as a file into Events
31980              * @param {HtmlEditor} this
31981              */
31982             autosave: true,
31983             /**
31984              * @event savedpreview
31985              * preview the saved version of htmlEditor
31986              * @param {HtmlEditor} this
31987              */
31988             savedpreview: true
31989         });
31990 };
31991
31992
31993 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
31994     
31995     
31996       /**
31997      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
31998      */
31999     toolbars : false,
32000     
32001      /**
32002     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32003     */
32004     btns : [],
32005    
32006      /**
32007      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
32008      *                        Roo.resizable.
32009      */
32010     resizable : false,
32011      /**
32012      * @cfg {Number} height (in pixels)
32013      */   
32014     height: 300,
32015    /**
32016      * @cfg {Number} width (in pixels)
32017      */   
32018     width: false,
32019     
32020     /**
32021      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32022      * 
32023      */
32024     stylesheets: false,
32025     
32026     // id of frame..
32027     frameId: false,
32028     
32029     // private properties
32030     validationEvent : false,
32031     deferHeight: true,
32032     initialized : false,
32033     activated : false,
32034     
32035     onFocus : Roo.emptyFn,
32036     iframePad:3,
32037     hideMode:'offsets',
32038     
32039     tbContainer : false,
32040     
32041     bodyCls : '',
32042     
32043     toolbarContainer :function() {
32044         return this.wrap.select('.x-html-editor-tb',true).first();
32045     },
32046
32047     /**
32048      * Protected method that will not generally be called directly. It
32049      * is called when the editor creates its toolbar. Override this method if you need to
32050      * add custom toolbar buttons.
32051      * @param {HtmlEditor} editor
32052      */
32053     createToolbar : function(){
32054         Roo.log('renewing');
32055         Roo.log("create toolbars");
32056         
32057         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
32058         this.toolbars[0].render(this.toolbarContainer());
32059         
32060         return;
32061         
32062 //        if (!editor.toolbars || !editor.toolbars.length) {
32063 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
32064 //        }
32065 //        
32066 //        for (var i =0 ; i < editor.toolbars.length;i++) {
32067 //            editor.toolbars[i] = Roo.factory(
32068 //                    typeof(editor.toolbars[i]) == 'string' ?
32069 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
32070 //                Roo.bootstrap.form.HtmlEditor);
32071 //            editor.toolbars[i].init(editor);
32072 //        }
32073     },
32074
32075      
32076     // private
32077     onRender : function(ct, position)
32078     {
32079        // Roo.log("Call onRender: " + this.xtype);
32080         var _t = this;
32081         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32082       
32083         this.wrap = this.inputEl().wrap({
32084             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32085         });
32086         
32087         this.editorcore.onRender(ct, position);
32088          
32089         if (this.resizable) {
32090             this.resizeEl = new Roo.Resizable(this.wrap, {
32091                 pinned : true,
32092                 wrap: true,
32093                 dynamic : true,
32094                 minHeight : this.height,
32095                 height: this.height,
32096                 handles : this.resizable,
32097                 width: this.width,
32098                 listeners : {
32099                     resize : function(r, w, h) {
32100                         _t.onResize(w,h); // -something
32101                     }
32102                 }
32103             });
32104             
32105         }
32106         this.createToolbar(this);
32107        
32108         
32109         if(!this.width && this.resizable){
32110             this.setSize(this.wrap.getSize());
32111         }
32112         if (this.resizeEl) {
32113             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
32114             // should trigger onReize..
32115         }
32116         
32117     },
32118
32119     // private
32120     onResize : function(w, h)
32121     {
32122         Roo.log('resize: ' +w + ',' + h );
32123         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32124         var ew = false;
32125         var eh = false;
32126         
32127         if(this.inputEl() ){
32128             if(typeof w == 'number'){
32129                 var aw = w - this.wrap.getFrameWidth('lr');
32130                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32131                 ew = aw;
32132             }
32133             if(typeof h == 'number'){
32134                  var tbh = -11;  // fixme it needs to tool bar size!
32135                 for (var i =0; i < this.toolbars.length;i++) {
32136                     // fixme - ask toolbars for heights?
32137                     tbh += this.toolbars[i].el.getHeight();
32138                     //if (this.toolbars[i].footer) {
32139                     //    tbh += this.toolbars[i].footer.el.getHeight();
32140                     //}
32141                 }
32142               
32143                 
32144                 
32145                 
32146                 
32147                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32148                 ah -= 5; // knock a few pixes off for look..
32149                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32150                 var eh = ah;
32151             }
32152         }
32153         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32154         this.editorcore.onResize(ew,eh);
32155         
32156     },
32157
32158     /**
32159      * Toggles the editor between standard and source edit mode.
32160      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32161      */
32162     toggleSourceEdit : function(sourceEditMode)
32163     {
32164         this.editorcore.toggleSourceEdit(sourceEditMode);
32165         
32166         if(this.editorcore.sourceEditMode){
32167             Roo.log('editor - showing textarea');
32168             
32169 //            Roo.log('in');
32170 //            Roo.log(this.syncValue());
32171             this.syncValue();
32172             this.inputEl().removeClass(['hide', 'x-hidden']);
32173             this.inputEl().dom.removeAttribute('tabIndex');
32174             this.inputEl().focus();
32175         }else{
32176             Roo.log('editor - hiding textarea');
32177 //            Roo.log('out')
32178 //            Roo.log(this.pushValue()); 
32179             this.pushValue();
32180             
32181             this.inputEl().addClass(['hide', 'x-hidden']);
32182             this.inputEl().dom.setAttribute('tabIndex', -1);
32183             //this.deferFocus();
32184         }
32185          
32186         if(this.resizable){
32187             this.setSize(this.wrap.getSize());
32188         }
32189         
32190         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32191     },
32192  
32193     // private (for BoxComponent)
32194     adjustSize : Roo.BoxComponent.prototype.adjustSize,
32195
32196     // private (for BoxComponent)
32197     getResizeEl : function(){
32198         return this.wrap;
32199     },
32200
32201     // private (for BoxComponent)
32202     getPositionEl : function(){
32203         return this.wrap;
32204     },
32205
32206     // private
32207     initEvents : function(){
32208         this.originalValue = this.getValue();
32209     },
32210
32211 //    /**
32212 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32213 //     * @method
32214 //     */
32215 //    markInvalid : Roo.emptyFn,
32216 //    /**
32217 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32218 //     * @method
32219 //     */
32220 //    clearInvalid : Roo.emptyFn,
32221
32222     setValue : function(v){
32223         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32224         this.editorcore.pushValue();
32225     },
32226
32227      
32228     // private
32229     deferFocus : function(){
32230         this.focus.defer(10, this);
32231     },
32232
32233     // doc'ed in Field
32234     focus : function(){
32235         this.editorcore.focus();
32236         
32237     },
32238       
32239
32240     // private
32241     onDestroy : function(){
32242         
32243         
32244         
32245         if(this.rendered){
32246             
32247             for (var i =0; i < this.toolbars.length;i++) {
32248                 // fixme - ask toolbars for heights?
32249                 this.toolbars[i].onDestroy();
32250             }
32251             
32252             this.wrap.dom.innerHTML = '';
32253             this.wrap.remove();
32254         }
32255     },
32256
32257     // private
32258     onFirstFocus : function(){
32259         //Roo.log("onFirstFocus");
32260         this.editorcore.onFirstFocus();
32261          for (var i =0; i < this.toolbars.length;i++) {
32262             this.toolbars[i].onFirstFocus();
32263         }
32264         
32265     },
32266     
32267     // private
32268     syncValue : function()
32269     {   
32270         this.editorcore.syncValue();
32271     },
32272     
32273     pushValue : function()
32274     {   
32275         this.editorcore.pushValue();
32276     }
32277      
32278     
32279     // hide stuff that is not compatible
32280     /**
32281      * @event blur
32282      * @hide
32283      */
32284     /**
32285      * @event change
32286      * @hide
32287      */
32288     /**
32289      * @event focus
32290      * @hide
32291      */
32292     /**
32293      * @event specialkey
32294      * @hide
32295      */
32296     /**
32297      * @cfg {String} fieldClass @hide
32298      */
32299     /**
32300      * @cfg {String} focusClass @hide
32301      */
32302     /**
32303      * @cfg {String} autoCreate @hide
32304      */
32305     /**
32306      * @cfg {String} inputType @hide
32307      */
32308      
32309     /**
32310      * @cfg {String} invalidText @hide
32311      */
32312     /**
32313      * @cfg {String} msgFx @hide
32314      */
32315     /**
32316      * @cfg {String} validateOnBlur @hide
32317      */
32318 });
32319  
32320     
32321    
32322    
32323    
32324       
32325 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
32326 /**
32327  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
32328  * @parent Roo.bootstrap.form.HtmlEditor
32329  * @extends Roo.bootstrap.nav.Simplebar
32330  * Basic Toolbar
32331  * 
32332  * @example
32333  * Usage:
32334  *
32335  new Roo.bootstrap.form.HtmlEditor({
32336     ....
32337     toolbars : [
32338         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
32339             disable : { fonts: 1 , format: 1, ..., ... , ...],
32340             btns : [ .... ]
32341         })
32342     }
32343      
32344  * 
32345  * @cfg {Object} disable List of elements to disable..
32346  * @cfg {Array} btns List of additional buttons.
32347  * 
32348  * 
32349  * NEEDS Extra CSS? 
32350  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32351  */
32352  
32353 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
32354 {
32355     
32356     Roo.apply(this, config);
32357     
32358     // default disabled, based on 'good practice'..
32359     this.disable = this.disable || {};
32360     Roo.applyIf(this.disable, {
32361         fontSize : true,
32362         colors : true,
32363         specialElements : true
32364     });
32365     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
32366     
32367     this.editor = config.editor;
32368     this.editorcore = config.editor.editorcore;
32369     
32370     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
32371     
32372     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32373     // dont call parent... till later.
32374 }
32375 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
32376      
32377     bar : true,
32378     
32379     editor : false,
32380     editorcore : false,
32381     
32382     
32383     formats : [
32384         "p" ,  
32385         "h1","h2","h3","h4","h5","h6", 
32386         "pre", "code", 
32387         "abbr", "acronym", "address", "cite", "samp", "var",
32388         'div','span'
32389     ],
32390     
32391     onRender : function(ct, position)
32392     {
32393        // Roo.log("Call onRender: " + this.xtype);
32394         
32395        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
32396        Roo.log(this.el);
32397        this.el.dom.style.marginBottom = '0';
32398        var _this = this;
32399        var editorcore = this.editorcore;
32400        var editor= this.editor;
32401        
32402        var children = [];
32403        var btn = function(id,cmd , toggle, handler, html){
32404        
32405             var  event = toggle ? 'toggle' : 'click';
32406        
32407             var a = {
32408                 size : 'sm',
32409                 xtype: 'Button',
32410                 xns: Roo.bootstrap,
32411                 //glyphicon : id,
32412                 fa: id,
32413                 cmd : id || cmd,
32414                 enableToggle:toggle !== false,
32415                 html : html || '',
32416                 pressed : toggle ? false : null,
32417                 listeners : {}
32418             };
32419             a.listeners[toggle ? 'toggle' : 'click'] = function() {
32420                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
32421             };
32422             children.push(a);
32423             return a;
32424        }
32425        
32426     //    var cb_box = function...
32427         
32428         var style = {
32429                 xtype: 'Button',
32430                 size : 'sm',
32431                 xns: Roo.bootstrap,
32432                 fa : 'font',
32433                 //html : 'submit'
32434                 menu : {
32435                     xtype: 'Menu',
32436                     xns: Roo.bootstrap,
32437                     items:  []
32438                 }
32439         };
32440         Roo.each(this.formats, function(f) {
32441             style.menu.items.push({
32442                 xtype :'MenuItem',
32443                 xns: Roo.bootstrap,
32444                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
32445                 tagname : f,
32446                 listeners : {
32447                     click : function()
32448                     {
32449                         editorcore.insertTag(this.tagname);
32450                         editor.focus();
32451                     }
32452                 }
32453                 
32454             });
32455         });
32456         children.push(style);   
32457         
32458         btn('bold',false,true);
32459         btn('italic',false,true);
32460         btn('align-left', 'justifyleft',true);
32461         btn('align-center', 'justifycenter',true);
32462         btn('align-right' , 'justifyright',true);
32463         btn('link', false, false, function(btn) {
32464             //Roo.log("create link?");
32465             var url = prompt(this.createLinkText, this.defaultLinkValue);
32466             if(url && url != 'http:/'+'/'){
32467                 this.editorcore.relayCmd('createlink', url);
32468             }
32469         }),
32470         btn('list','insertunorderedlist',true);
32471         btn('pencil', false,true, function(btn){
32472                 Roo.log(this);
32473                 this.toggleSourceEdit(btn.pressed);
32474         });
32475         
32476         if (this.editor.btns.length > 0) {
32477             for (var i = 0; i<this.editor.btns.length; i++) {
32478                 children.push(this.editor.btns[i]);
32479             }
32480         }
32481         
32482         /*
32483         var cog = {
32484                 xtype: 'Button',
32485                 size : 'sm',
32486                 xns: Roo.bootstrap,
32487                 glyphicon : 'cog',
32488                 //html : 'submit'
32489                 menu : {
32490                     xtype: 'Menu',
32491                     xns: Roo.bootstrap,
32492                     items:  []
32493                 }
32494         };
32495         
32496         cog.menu.items.push({
32497             xtype :'MenuItem',
32498             xns: Roo.bootstrap,
32499             html : Clean styles,
32500             tagname : f,
32501             listeners : {
32502                 click : function()
32503                 {
32504                     editorcore.insertTag(this.tagname);
32505                     editor.focus();
32506                 }
32507             }
32508             
32509         });
32510        */
32511         
32512          
32513        this.xtype = 'NavSimplebar';
32514         
32515         for(var i=0;i< children.length;i++) {
32516             
32517             this.buttons.add(this.addxtypeChild(children[i]));
32518             
32519         }
32520         
32521         editor.on('editorevent', this.updateToolbar, this);
32522     },
32523     onBtnClick : function(id)
32524     {
32525        this.editorcore.relayCmd(id);
32526        this.editorcore.focus();
32527     },
32528     
32529     /**
32530      * Protected method that will not generally be called directly. It triggers
32531      * a toolbar update by reading the markup state of the current selection in the editor.
32532      */
32533     updateToolbar: function(){
32534
32535         if(!this.editorcore.activated){
32536             this.editor.onFirstFocus(); // is this neeed?
32537             return;
32538         }
32539
32540         var btns = this.buttons; 
32541         var doc = this.editorcore.doc;
32542         btns.get('bold').setActive(doc.queryCommandState('bold'));
32543         btns.get('italic').setActive(doc.queryCommandState('italic'));
32544         //btns.get('underline').setActive(doc.queryCommandState('underline'));
32545         
32546         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
32547         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
32548         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
32549         
32550         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
32551         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
32552          /*
32553         
32554         var ans = this.editorcore.getAllAncestors();
32555         if (this.formatCombo) {
32556             
32557             
32558             var store = this.formatCombo.store;
32559             this.formatCombo.setValue("");
32560             for (var i =0; i < ans.length;i++) {
32561                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
32562                     // select it..
32563                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
32564                     break;
32565                 }
32566             }
32567         }
32568         
32569         
32570         
32571         // hides menus... - so this cant be on a menu...
32572         Roo.bootstrap.MenuMgr.hideAll();
32573         */
32574         Roo.bootstrap.menu.Manager.hideAll();
32575         //this.editorsyncValue();
32576     },
32577     onFirstFocus: function() {
32578         this.buttons.each(function(item){
32579            item.enable();
32580         });
32581     },
32582     toggleSourceEdit : function(sourceEditMode){
32583         
32584           
32585         if(sourceEditMode){
32586             Roo.log("disabling buttons");
32587            this.buttons.each( function(item){
32588                 if(item.cmd != 'pencil'){
32589                     item.disable();
32590                 }
32591             });
32592           
32593         }else{
32594             Roo.log("enabling buttons");
32595             if(this.editorcore.initialized){
32596                 this.buttons.each( function(item){
32597                     item.enable();
32598                 });
32599             }
32600             
32601         }
32602         Roo.log("calling toggole on editor");
32603         // tell the editor that it's been pressed..
32604         this.editor.toggleSourceEdit(sourceEditMode);
32605        
32606     }
32607 });
32608
32609
32610
32611
32612  
32613 /*
32614  * - LGPL
32615  */
32616
32617 /**
32618  * @class Roo.bootstrap.form.Markdown
32619  * @extends Roo.bootstrap.form.TextArea
32620  * Bootstrap Showdown editable area
32621  * @cfg {string} content
32622  * 
32623  * @constructor
32624  * Create a new Showdown
32625  */
32626
32627 Roo.bootstrap.form.Markdown = function(config){
32628     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
32629    
32630 };
32631
32632 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
32633     
32634     editing :false,
32635     
32636     initEvents : function()
32637     {
32638         
32639         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
32640         this.markdownEl = this.el.createChild({
32641             cls : 'roo-markdown-area'
32642         });
32643         this.inputEl().addClass('d-none');
32644         if (this.getValue() == '') {
32645             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
32646             
32647         } else {
32648             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
32649         }
32650         this.markdownEl.on('click', this.toggleTextEdit, this);
32651         this.on('blur', this.toggleTextEdit, this);
32652         this.on('specialkey', this.resizeTextArea, this);
32653     },
32654     
32655     toggleTextEdit : function()
32656     {
32657         var sh = this.markdownEl.getHeight();
32658         this.inputEl().addClass('d-none');
32659         this.markdownEl.addClass('d-none');
32660         if (!this.editing) {
32661             // show editor?
32662             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
32663             this.inputEl().removeClass('d-none');
32664             this.inputEl().focus();
32665             this.editing = true;
32666             return;
32667         }
32668         // show showdown...
32669         this.updateMarkdown();
32670         this.markdownEl.removeClass('d-none');
32671         this.editing = false;
32672         return;
32673     },
32674     updateMarkdown : function()
32675     {
32676         if (this.getValue() == '') {
32677             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
32678             return;
32679         }
32680  
32681         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
32682     },
32683     
32684     resizeTextArea: function () {
32685         
32686         var sh = 100;
32687         Roo.log([sh, this.getValue().split("\n").length * 30]);
32688         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
32689     },
32690     setValue : function(val)
32691     {
32692         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
32693         if (!this.editing) {
32694             this.updateMarkdown();
32695         }
32696         
32697     },
32698     focus : function()
32699     {
32700         if (!this.editing) {
32701             this.toggleTextEdit();
32702         }
32703         
32704     }
32705
32706
32707 });/*
32708  * Based on:
32709  * Ext JS Library 1.1.1
32710  * Copyright(c) 2006-2007, Ext JS, LLC.
32711  *
32712  * Originally Released Under LGPL - original licence link has changed is not relivant.
32713  *
32714  * Fork - LGPL
32715  * <script type="text/javascript">
32716  */
32717  
32718 /**
32719  * @class Roo.bootstrap.PagingToolbar
32720  * @extends Roo.bootstrap.nav.Simplebar
32721  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
32722  * @constructor
32723  * Create a new PagingToolbar
32724  * @param {Object} config The config object
32725  * @param {Roo.data.Store} store
32726  */
32727 Roo.bootstrap.PagingToolbar = function(config)
32728 {
32729     // old args format still supported... - xtype is prefered..
32730         // created from xtype...
32731     
32732     this.ds = config.dataSource;
32733     
32734     if (config.store && !this.ds) {
32735         this.store= Roo.factory(config.store, Roo.data);
32736         this.ds = this.store;
32737         this.ds.xmodule = this.xmodule || false;
32738     }
32739     
32740     this.toolbarItems = [];
32741     if (config.items) {
32742         this.toolbarItems = config.items;
32743     }
32744     
32745     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
32746     
32747     this.cursor = 0;
32748     
32749     if (this.ds) { 
32750         this.bind(this.ds);
32751     }
32752     
32753     if (Roo.bootstrap.version == 4) {
32754         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
32755     } else {
32756         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
32757     }
32758     
32759 };
32760
32761 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
32762     /**
32763      * @cfg {Roo.bootstrap.Button} buttons[]
32764      * Buttons for the toolbar
32765      */
32766      /**
32767      * @cfg {Roo.data.Store} store
32768      * The underlying data store providing the paged data
32769      */
32770     /**
32771      * @cfg {String/HTMLElement/Element} container
32772      * container The id or element that will contain the toolbar
32773      */
32774     /**
32775      * @cfg {Boolean} displayInfo
32776      * True to display the displayMsg (defaults to false)
32777      */
32778     /**
32779      * @cfg {Number} pageSize
32780      * The number of records to display per page (defaults to 20)
32781      */
32782     pageSize: 20,
32783     /**
32784      * @cfg {String} displayMsg
32785      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32786      */
32787     displayMsg : 'Displaying {0} - {1} of {2}',
32788     /**
32789      * @cfg {String} emptyMsg
32790      * The message to display when no records are found (defaults to "No data to display")
32791      */
32792     emptyMsg : 'No data to display',
32793     /**
32794      * Customizable piece of the default paging text (defaults to "Page")
32795      * @type String
32796      */
32797     beforePageText : "Page",
32798     /**
32799      * Customizable piece of the default paging text (defaults to "of %0")
32800      * @type String
32801      */
32802     afterPageText : "of {0}",
32803     /**
32804      * Customizable piece of the default paging text (defaults to "First Page")
32805      * @type String
32806      */
32807     firstText : "First Page",
32808     /**
32809      * Customizable piece of the default paging text (defaults to "Previous Page")
32810      * @type String
32811      */
32812     prevText : "Previous Page",
32813     /**
32814      * Customizable piece of the default paging text (defaults to "Next Page")
32815      * @type String
32816      */
32817     nextText : "Next Page",
32818     /**
32819      * Customizable piece of the default paging text (defaults to "Last Page")
32820      * @type String
32821      */
32822     lastText : "Last Page",
32823     /**
32824      * Customizable piece of the default paging text (defaults to "Refresh")
32825      * @type String
32826      */
32827     refreshText : "Refresh",
32828
32829     buttons : false,
32830     // private
32831     onRender : function(ct, position) 
32832     {
32833         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
32834         this.navgroup.parentId = this.id;
32835         this.navgroup.onRender(this.el, null);
32836         // add the buttons to the navgroup
32837         
32838         if(this.displayInfo){
32839             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
32840             this.displayEl = this.el.select('.x-paging-info', true).first();
32841 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
32842 //            this.displayEl = navel.el.select('span',true).first();
32843         }
32844         
32845         var _this = this;
32846         
32847         if(this.buttons){
32848             Roo.each(_this.buttons, function(e){ // this might need to use render????
32849                Roo.factory(e).render(_this.el);
32850             });
32851         }
32852             
32853         Roo.each(_this.toolbarItems, function(e) {
32854             _this.navgroup.addItem(e);
32855         });
32856         
32857         
32858         this.first = this.navgroup.addItem({
32859             tooltip: this.firstText,
32860             cls: "prev btn-outline-secondary",
32861             html : ' <i class="fa fa-step-backward"></i>',
32862             disabled: true,
32863             preventDefault: true,
32864             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
32865         });
32866         
32867         this.prev =  this.navgroup.addItem({
32868             tooltip: this.prevText,
32869             cls: "prev btn-outline-secondary",
32870             html : ' <i class="fa fa-backward"></i>',
32871             disabled: true,
32872             preventDefault: true,
32873             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
32874         });
32875     //this.addSeparator();
32876         
32877         
32878         var field = this.navgroup.addItem( {
32879             tagtype : 'span',
32880             cls : 'x-paging-position  btn-outline-secondary',
32881              disabled: true,
32882             html : this.beforePageText  +
32883                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
32884                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
32885          } ); //?? escaped?
32886         
32887         this.field = field.el.select('input', true).first();
32888         this.field.on("keydown", this.onPagingKeydown, this);
32889         this.field.on("focus", function(){this.dom.select();});
32890     
32891     
32892         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
32893         //this.field.setHeight(18);
32894         //this.addSeparator();
32895         this.next = this.navgroup.addItem({
32896             tooltip: this.nextText,
32897             cls: "next btn-outline-secondary",
32898             html : ' <i class="fa fa-forward"></i>',
32899             disabled: true,
32900             preventDefault: true,
32901             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
32902         });
32903         this.last = this.navgroup.addItem({
32904             tooltip: this.lastText,
32905             html : ' <i class="fa fa-step-forward"></i>',
32906             cls: "next btn-outline-secondary",
32907             disabled: true,
32908             preventDefault: true,
32909             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
32910         });
32911     //this.addSeparator();
32912         this.loading = this.navgroup.addItem({
32913             tooltip: this.refreshText,
32914             cls: "btn-outline-secondary",
32915             html : ' <i class="fa fa-refresh"></i>',
32916             preventDefault: true,
32917             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
32918         });
32919         
32920     },
32921
32922     // private
32923     updateInfo : function(){
32924         if(this.displayEl){
32925             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
32926             var msg = count == 0 ?
32927                 this.emptyMsg :
32928                 String.format(
32929                     this.displayMsg,
32930                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
32931                 );
32932             this.displayEl.update(msg);
32933         }
32934     },
32935
32936     // private
32937     onLoad : function(ds, r, o)
32938     {
32939         this.cursor = o.params && o.params.start ? o.params.start : 0;
32940         
32941         var d = this.getPageData(),
32942             ap = d.activePage,
32943             ps = d.pages;
32944         
32945         
32946         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
32947         this.field.dom.value = ap;
32948         this.first.setDisabled(ap == 1);
32949         this.prev.setDisabled(ap == 1);
32950         this.next.setDisabled(ap == ps);
32951         this.last.setDisabled(ap == ps);
32952         this.loading.enable();
32953         this.updateInfo();
32954     },
32955
32956     // private
32957     getPageData : function(){
32958         var total = this.ds.getTotalCount();
32959         return {
32960             total : total,
32961             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32962             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32963         };
32964     },
32965
32966     // private
32967     onLoadError : function(proxy, o){
32968         this.loading.enable();
32969         if (this.ds.events.loadexception.listeners.length  < 2) {
32970             // nothing has been assigned to loadexception except this...
32971             // so 
32972             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
32973
32974         }
32975     },
32976
32977     // private
32978     onPagingKeydown : function(e){
32979         var k = e.getKey();
32980         var d = this.getPageData();
32981         if(k == e.RETURN){
32982             var v = this.field.dom.value, pageNum;
32983             if(!v || isNaN(pageNum = parseInt(v, 10))){
32984                 this.field.dom.value = d.activePage;
32985                 return;
32986             }
32987             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32988             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32989             e.stopEvent();
32990         }
32991         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))
32992         {
32993           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32994           this.field.dom.value = pageNum;
32995           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32996           e.stopEvent();
32997         }
32998         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32999         {
33000           var v = this.field.dom.value, pageNum; 
33001           var increment = (e.shiftKey) ? 10 : 1;
33002           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33003                 increment *= -1;
33004           }
33005           if(!v || isNaN(pageNum = parseInt(v, 10))) {
33006             this.field.dom.value = d.activePage;
33007             return;
33008           }
33009           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33010           {
33011             this.field.dom.value = parseInt(v, 10) + increment;
33012             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33013             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33014           }
33015           e.stopEvent();
33016         }
33017     },
33018
33019     // private
33020     beforeLoad : function(){
33021         if(this.loading){
33022             this.loading.disable();
33023         }
33024     },
33025
33026     // private
33027     onClick : function(which){
33028         
33029         var ds = this.ds;
33030         if (!ds) {
33031             return;
33032         }
33033         
33034         switch(which){
33035             case "first":
33036                 ds.load({params:{start: 0, limit: this.pageSize}});
33037             break;
33038             case "prev":
33039                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33040             break;
33041             case "next":
33042                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33043             break;
33044             case "last":
33045                 var total = ds.getTotalCount();
33046                 var extra = total % this.pageSize;
33047                 var lastStart = extra ? (total - extra) : total-this.pageSize;
33048                 ds.load({params:{start: lastStart, limit: this.pageSize}});
33049             break;
33050             case "refresh":
33051                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33052             break;
33053         }
33054     },
33055
33056     /**
33057      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33058      * @param {Roo.data.Store} store The data store to unbind
33059      */
33060     unbind : function(ds){
33061         ds.un("beforeload", this.beforeLoad, this);
33062         ds.un("load", this.onLoad, this);
33063         ds.un("loadexception", this.onLoadError, this);
33064         ds.un("remove", this.updateInfo, this);
33065         ds.un("add", this.updateInfo, this);
33066         this.ds = undefined;
33067     },
33068
33069     /**
33070      * Binds the paging toolbar to the specified {@link Roo.data.Store}
33071      * @param {Roo.data.Store} store The data store to bind
33072      */
33073     bind : function(ds){
33074         ds.on("beforeload", this.beforeLoad, this);
33075         ds.on("load", this.onLoad, this);
33076         ds.on("loadexception", this.onLoadError, this);
33077         ds.on("remove", this.updateInfo, this);
33078         ds.on("add", this.updateInfo, this);
33079         this.ds = ds;
33080     }
33081 });/*
33082  * - LGPL
33083  *
33084  * element
33085  * 
33086  */
33087
33088 /**
33089  * @class Roo.bootstrap.MessageBar
33090  * @extends Roo.bootstrap.Component
33091  * Bootstrap MessageBar class
33092  * @cfg {String} html contents of the MessageBar
33093  * @cfg {String} weight (info | success | warning | danger) default info
33094  * @cfg {String} beforeClass insert the bar before the given class
33095  * @cfg {Boolean} closable (true | false) default false
33096  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33097  * 
33098  * @constructor
33099  * Create a new Element
33100  * @param {Object} config The config object
33101  */
33102
33103 Roo.bootstrap.MessageBar = function(config){
33104     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33105 };
33106
33107 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
33108     
33109     html: '',
33110     weight: 'info',
33111     closable: false,
33112     fixed: false,
33113     beforeClass: 'bootstrap-sticky-wrap',
33114     
33115     getAutoCreate : function(){
33116         
33117         var cfg = {
33118             tag: 'div',
33119             cls: 'alert alert-dismissable alert-' + this.weight,
33120             cn: [
33121                 {
33122                     tag: 'span',
33123                     cls: 'message',
33124                     html: this.html || ''
33125                 }
33126             ]
33127         };
33128         
33129         if(this.fixed){
33130             cfg.cls += ' alert-messages-fixed';
33131         }
33132         
33133         if(this.closable){
33134             cfg.cn.push({
33135                 tag: 'button',
33136                 cls: 'close',
33137                 html: 'x'
33138             });
33139         }
33140         
33141         return cfg;
33142     },
33143     
33144     onRender : function(ct, position)
33145     {
33146         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33147         
33148         if(!this.el){
33149             var cfg = Roo.apply({},  this.getAutoCreate());
33150             cfg.id = Roo.id();
33151             
33152             if (this.cls) {
33153                 cfg.cls += ' ' + this.cls;
33154             }
33155             if (this.style) {
33156                 cfg.style = this.style;
33157             }
33158             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33159             
33160             this.el.setVisibilityMode(Roo.Element.DISPLAY);
33161         }
33162         
33163         this.el.select('>button.close').on('click', this.hide, this);
33164         
33165     },
33166     
33167     show : function()
33168     {
33169         if (!this.rendered) {
33170             this.render();
33171         }
33172         
33173         this.el.show();
33174         
33175         this.fireEvent('show', this);
33176         
33177     },
33178     
33179     hide : function()
33180     {
33181         if (!this.rendered) {
33182             this.render();
33183         }
33184         
33185         this.el.hide();
33186         
33187         this.fireEvent('hide', this);
33188     },
33189     
33190     update : function()
33191     {
33192 //        var e = this.el.dom.firstChild;
33193 //        
33194 //        if(this.closable){
33195 //            e = e.nextSibling;
33196 //        }
33197 //        
33198 //        e.data = this.html || '';
33199
33200         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
33201     }
33202    
33203 });
33204
33205  
33206
33207      /*
33208  * - LGPL
33209  *
33210  * Graph
33211  * 
33212  */
33213
33214
33215 /**
33216  * @class Roo.bootstrap.Graph
33217  * @extends Roo.bootstrap.Component
33218  * Bootstrap Graph class
33219 > Prameters
33220  -sm {number} sm 4
33221  -md {number} md 5
33222  @cfg {String} graphtype  bar | vbar | pie
33223  @cfg {number} g_x coodinator | centre x (pie)
33224  @cfg {number} g_y coodinator | centre y (pie)
33225  @cfg {number} g_r radius (pie)
33226  @cfg {number} g_height height of the chart (respected by all elements in the set)
33227  @cfg {number} g_width width of the chart (respected by all elements in the set)
33228  @cfg {Object} title The title of the chart
33229     
33230  -{Array}  values
33231  -opts (object) options for the chart 
33232      o {
33233      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
33234      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
33235      o vgutter (number)
33236      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.
33237      o stacked (boolean) whether or not to tread values as in a stacked bar chart
33238      o to
33239      o stretch (boolean)
33240      o }
33241  -opts (object) options for the pie
33242      o{
33243      o cut
33244      o startAngle (number)
33245      o endAngle (number)
33246      } 
33247  *
33248  * @constructor
33249  * Create a new Input
33250  * @param {Object} config The config object
33251  */
33252
33253 Roo.bootstrap.Graph = function(config){
33254     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
33255     
33256     this.addEvents({
33257         // img events
33258         /**
33259          * @event click
33260          * The img click event for the img.
33261          * @param {Roo.EventObject} e
33262          */
33263         "click" : true
33264     });
33265 };
33266
33267 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
33268     
33269     sm: 4,
33270     md: 5,
33271     graphtype: 'bar',
33272     g_height: 250,
33273     g_width: 400,
33274     g_x: 50,
33275     g_y: 50,
33276     g_r: 30,
33277     opts:{
33278         //g_colors: this.colors,
33279         g_type: 'soft',
33280         g_gutter: '20%'
33281
33282     },
33283     title : false,
33284
33285     getAutoCreate : function(){
33286         
33287         var cfg = {
33288             tag: 'div',
33289             html : null
33290         };
33291         
33292         
33293         return  cfg;
33294     },
33295
33296     onRender : function(ct,position){
33297         
33298         
33299         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
33300         
33301         if (typeof(Raphael) == 'undefined') {
33302             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
33303             return;
33304         }
33305         
33306         this.raphael = Raphael(this.el.dom);
33307         
33308                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33309                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33310                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33311                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
33312                 /*
33313                 r.text(160, 10, "Single Series Chart").attr(txtattr);
33314                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
33315                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
33316                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
33317                 
33318                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
33319                 r.barchart(330, 10, 300, 220, data1);
33320                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
33321                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
33322                 */
33323                 
33324                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33325                 // r.barchart(30, 30, 560, 250,  xdata, {
33326                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
33327                 //     axis : "0 0 1 1",
33328                 //     axisxlabels :  xdata
33329                 //     //yvalues : cols,
33330                    
33331                 // });
33332 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33333 //        
33334 //        this.load(null,xdata,{
33335 //                axis : "0 0 1 1",
33336 //                axisxlabels :  xdata
33337 //                });
33338
33339     },
33340
33341     load : function(graphtype,xdata,opts)
33342     {
33343         this.raphael.clear();
33344         if(!graphtype) {
33345             graphtype = this.graphtype;
33346         }
33347         if(!opts){
33348             opts = this.opts;
33349         }
33350         var r = this.raphael,
33351             fin = function () {
33352                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
33353             },
33354             fout = function () {
33355                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
33356             },
33357             pfin = function() {
33358                 this.sector.stop();
33359                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
33360
33361                 if (this.label) {
33362                     this.label[0].stop();
33363                     this.label[0].attr({ r: 7.5 });
33364                     this.label[1].attr({ "font-weight": 800 });
33365                 }
33366             },
33367             pfout = function() {
33368                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
33369
33370                 if (this.label) {
33371                     this.label[0].animate({ r: 5 }, 500, "bounce");
33372                     this.label[1].attr({ "font-weight": 400 });
33373                 }
33374             };
33375
33376         switch(graphtype){
33377             case 'bar':
33378                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33379                 break;
33380             case 'hbar':
33381                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33382                 break;
33383             case 'pie':
33384 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
33385 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
33386 //            
33387                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
33388                 
33389                 break;
33390
33391         }
33392         
33393         if(this.title){
33394             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
33395         }
33396         
33397     },
33398     
33399     setTitle: function(o)
33400     {
33401         this.title = o;
33402     },
33403     
33404     initEvents: function() {
33405         
33406         if(!this.href){
33407             this.el.on('click', this.onClick, this);
33408         }
33409     },
33410     
33411     onClick : function(e)
33412     {
33413         Roo.log('img onclick');
33414         this.fireEvent('click', this, e);
33415     }
33416    
33417 });
33418
33419  
33420 Roo.bootstrap.dash = {};/*
33421  * - LGPL
33422  *
33423  * numberBox
33424  * 
33425  */
33426 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33427
33428 /**
33429  * @class Roo.bootstrap.dash.NumberBox
33430  * @extends Roo.bootstrap.Component
33431  * Bootstrap NumberBox class
33432  * @cfg {String} headline Box headline
33433  * @cfg {String} content Box content
33434  * @cfg {String} icon Box icon
33435  * @cfg {String} footer Footer text
33436  * @cfg {String} fhref Footer href
33437  * 
33438  * @constructor
33439  * Create a new NumberBox
33440  * @param {Object} config The config object
33441  */
33442
33443
33444 Roo.bootstrap.dash.NumberBox = function(config){
33445     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
33446     
33447 };
33448
33449 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
33450     
33451     headline : '',
33452     content : '',
33453     icon : '',
33454     footer : '',
33455     fhref : '',
33456     ficon : '',
33457     
33458     getAutoCreate : function(){
33459         
33460         var cfg = {
33461             tag : 'div',
33462             cls : 'small-box ',
33463             cn : [
33464                 {
33465                     tag : 'div',
33466                     cls : 'inner',
33467                     cn :[
33468                         {
33469                             tag : 'h3',
33470                             cls : 'roo-headline',
33471                             html : this.headline
33472                         },
33473                         {
33474                             tag : 'p',
33475                             cls : 'roo-content',
33476                             html : this.content
33477                         }
33478                     ]
33479                 }
33480             ]
33481         };
33482         
33483         if(this.icon){
33484             cfg.cn.push({
33485                 tag : 'div',
33486                 cls : 'icon',
33487                 cn :[
33488                     {
33489                         tag : 'i',
33490                         cls : 'ion ' + this.icon
33491                     }
33492                 ]
33493             });
33494         }
33495         
33496         if(this.footer){
33497             var footer = {
33498                 tag : 'a',
33499                 cls : 'small-box-footer',
33500                 href : this.fhref || '#',
33501                 html : this.footer
33502             };
33503             
33504             cfg.cn.push(footer);
33505             
33506         }
33507         
33508         return  cfg;
33509     },
33510
33511     onRender : function(ct,position){
33512         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
33513
33514
33515        
33516                 
33517     },
33518
33519     setHeadline: function (value)
33520     {
33521         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
33522     },
33523     
33524     setFooter: function (value, href)
33525     {
33526         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
33527         
33528         if(href){
33529             this.el.select('a.small-box-footer',true).first().attr('href', href);
33530         }
33531         
33532     },
33533
33534     setContent: function (value)
33535     {
33536         this.el.select('.roo-content',true).first().dom.innerHTML = value;
33537     },
33538
33539     initEvents: function() 
33540     {   
33541         
33542     }
33543     
33544 });
33545
33546  
33547 /*
33548  * - LGPL
33549  *
33550  * TabBox
33551  * 
33552  */
33553 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33554
33555 /**
33556  * @class Roo.bootstrap.dash.TabBox
33557  * @extends Roo.bootstrap.Component
33558  * @children Roo.bootstrap.dash.TabPane
33559  * Bootstrap TabBox class
33560  * @cfg {String} title Title of the TabBox
33561  * @cfg {String} icon Icon of the TabBox
33562  * @cfg {Boolean} showtabs (true|false) show the tabs default true
33563  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
33564  * 
33565  * @constructor
33566  * Create a new TabBox
33567  * @param {Object} config The config object
33568  */
33569
33570
33571 Roo.bootstrap.dash.TabBox = function(config){
33572     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
33573     this.addEvents({
33574         // raw events
33575         /**
33576          * @event addpane
33577          * When a pane is added
33578          * @param {Roo.bootstrap.dash.TabPane} pane
33579          */
33580         "addpane" : true,
33581         /**
33582          * @event activatepane
33583          * When a pane is activated
33584          * @param {Roo.bootstrap.dash.TabPane} pane
33585          */
33586         "activatepane" : true
33587         
33588          
33589     });
33590     
33591     this.panes = [];
33592 };
33593
33594 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
33595
33596     title : '',
33597     icon : false,
33598     showtabs : true,
33599     tabScrollable : false,
33600     
33601     getChildContainer : function()
33602     {
33603         return this.el.select('.tab-content', true).first();
33604     },
33605     
33606     getAutoCreate : function(){
33607         
33608         var header = {
33609             tag: 'li',
33610             cls: 'pull-left header',
33611             html: this.title,
33612             cn : []
33613         };
33614         
33615         if(this.icon){
33616             header.cn.push({
33617                 tag: 'i',
33618                 cls: 'fa ' + this.icon
33619             });
33620         }
33621         
33622         var h = {
33623             tag: 'ul',
33624             cls: 'nav nav-tabs pull-right',
33625             cn: [
33626                 header
33627             ]
33628         };
33629         
33630         if(this.tabScrollable){
33631             h = {
33632                 tag: 'div',
33633                 cls: 'tab-header',
33634                 cn: [
33635                     {
33636                         tag: 'ul',
33637                         cls: 'nav nav-tabs pull-right',
33638                         cn: [
33639                             header
33640                         ]
33641                     }
33642                 ]
33643             };
33644         }
33645         
33646         var cfg = {
33647             tag: 'div',
33648             cls: 'nav-tabs-custom',
33649             cn: [
33650                 h,
33651                 {
33652                     tag: 'div',
33653                     cls: 'tab-content no-padding',
33654                     cn: []
33655                 }
33656             ]
33657         };
33658
33659         return  cfg;
33660     },
33661     initEvents : function()
33662     {
33663         //Roo.log('add add pane handler');
33664         this.on('addpane', this.onAddPane, this);
33665     },
33666      /**
33667      * Updates the box title
33668      * @param {String} html to set the title to.
33669      */
33670     setTitle : function(value)
33671     {
33672         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
33673     },
33674     onAddPane : function(pane)
33675     {
33676         this.panes.push(pane);
33677         //Roo.log('addpane');
33678         //Roo.log(pane);
33679         // tabs are rendere left to right..
33680         if(!this.showtabs){
33681             return;
33682         }
33683         
33684         var ctr = this.el.select('.nav-tabs', true).first();
33685          
33686          
33687         var existing = ctr.select('.nav-tab',true);
33688         var qty = existing.getCount();;
33689         
33690         
33691         var tab = ctr.createChild({
33692             tag : 'li',
33693             cls : 'nav-tab' + (qty ? '' : ' active'),
33694             cn : [
33695                 {
33696                     tag : 'a',
33697                     href:'#',
33698                     html : pane.title
33699                 }
33700             ]
33701         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
33702         pane.tab = tab;
33703         
33704         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
33705         if (!qty) {
33706             pane.el.addClass('active');
33707         }
33708         
33709                 
33710     },
33711     onTabClick : function(ev,un,ob,pane)
33712     {
33713         //Roo.log('tab - prev default');
33714         ev.preventDefault();
33715         
33716         
33717         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
33718         pane.tab.addClass('active');
33719         //Roo.log(pane.title);
33720         this.getChildContainer().select('.tab-pane',true).removeClass('active');
33721         // technically we should have a deactivate event.. but maybe add later.
33722         // and it should not de-activate the selected tab...
33723         this.fireEvent('activatepane', pane);
33724         pane.el.addClass('active');
33725         pane.fireEvent('activate');
33726         
33727         
33728     },
33729     
33730     getActivePane : function()
33731     {
33732         var r = false;
33733         Roo.each(this.panes, function(p) {
33734             if(p.el.hasClass('active')){
33735                 r = p;
33736                 return false;
33737             }
33738             
33739             return;
33740         });
33741         
33742         return r;
33743     }
33744     
33745     
33746 });
33747
33748  
33749 /*
33750  * - LGPL
33751  *
33752  * Tab pane
33753  * 
33754  */
33755 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33756 /**
33757  * @class Roo.bootstrap.TabPane
33758  * @extends Roo.bootstrap.Component
33759  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
33760  * Bootstrap TabPane class
33761  * @cfg {Boolean} active (false | true) Default false
33762  * @cfg {String} title title of panel
33763
33764  * 
33765  * @constructor
33766  * Create a new TabPane
33767  * @param {Object} config The config object
33768  */
33769
33770 Roo.bootstrap.dash.TabPane = function(config){
33771     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
33772     
33773     this.addEvents({
33774         // raw events
33775         /**
33776          * @event activate
33777          * When a pane is activated
33778          * @param {Roo.bootstrap.dash.TabPane} pane
33779          */
33780         "activate" : true
33781          
33782     });
33783 };
33784
33785 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
33786     
33787     active : false,
33788     title : '',
33789     
33790     // the tabBox that this is attached to.
33791     tab : false,
33792      
33793     getAutoCreate : function() 
33794     {
33795         var cfg = {
33796             tag: 'div',
33797             cls: 'tab-pane'
33798         };
33799         
33800         if(this.active){
33801             cfg.cls += ' active';
33802         }
33803         
33804         return cfg;
33805     },
33806     initEvents  : function()
33807     {
33808         //Roo.log('trigger add pane handler');
33809         this.parent().fireEvent('addpane', this)
33810     },
33811     
33812      /**
33813      * Updates the tab title 
33814      * @param {String} html to set the title to.
33815      */
33816     setTitle: function(str)
33817     {
33818         if (!this.tab) {
33819             return;
33820         }
33821         this.title = str;
33822         this.tab.select('a', true).first().dom.innerHTML = str;
33823         
33824     }
33825     
33826     
33827     
33828 });
33829
33830  
33831
33832
33833  /*
33834  * - LGPL
33835  *
33836  * Tooltip
33837  * 
33838  */
33839
33840 /**
33841  * @class Roo.bootstrap.Tooltip
33842  * Bootstrap Tooltip class
33843  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
33844  * to determine which dom element triggers the tooltip.
33845  * 
33846  * It needs to add support for additional attributes like tooltip-position
33847  * 
33848  * @constructor
33849  * Create a new Toolti
33850  * @param {Object} config The config object
33851  */
33852
33853 Roo.bootstrap.Tooltip = function(config){
33854     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
33855     
33856     this.alignment = Roo.bootstrap.Tooltip.alignment;
33857     
33858     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
33859         this.alignment = config.alignment;
33860     }
33861     
33862 };
33863
33864 Roo.apply(Roo.bootstrap.Tooltip, {
33865     /**
33866      * @function init initialize tooltip monitoring.
33867      * @static
33868      */
33869     currentEl : false,
33870     currentTip : false,
33871     currentRegion : false,
33872     
33873     //  init : delay?
33874     
33875     init : function()
33876     {
33877         Roo.get(document).on('mouseover', this.enter ,this);
33878         Roo.get(document).on('mouseout', this.leave, this);
33879          
33880         
33881         this.currentTip = new Roo.bootstrap.Tooltip();
33882     },
33883     
33884     enter : function(ev)
33885     {
33886         var dom = ev.getTarget();
33887         
33888         //Roo.log(['enter',dom]);
33889         var el = Roo.fly(dom);
33890         if (this.currentEl) {
33891             //Roo.log(dom);
33892             //Roo.log(this.currentEl);
33893             //Roo.log(this.currentEl.contains(dom));
33894             if (this.currentEl == el) {
33895                 return;
33896             }
33897             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
33898                 return;
33899             }
33900
33901         }
33902         
33903         if (this.currentTip.el) {
33904             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
33905         }    
33906         //Roo.log(ev);
33907         
33908         if(!el || el.dom == document){
33909             return;
33910         }
33911         
33912         var bindEl = el; 
33913         var pel = false;
33914         if (!el.attr('tooltip')) {
33915             pel = el.findParent("[tooltip]");
33916             if (pel) {
33917                 bindEl = Roo.get(pel);
33918             }
33919         }
33920         
33921        
33922         
33923         // you can not look for children, as if el is the body.. then everythign is the child..
33924         if (!pel && !el.attr('tooltip')) { //
33925             if (!el.select("[tooltip]").elements.length) {
33926                 return;
33927             }
33928             // is the mouse over this child...?
33929             bindEl = el.select("[tooltip]").first();
33930             var xy = ev.getXY();
33931             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
33932                 //Roo.log("not in region.");
33933                 return;
33934             }
33935             //Roo.log("child element over..");
33936             
33937         }
33938         this.currentEl = el;
33939         this.currentTip.bind(bindEl);
33940         this.currentRegion = Roo.lib.Region.getRegion(dom);
33941         this.currentTip.enter();
33942         
33943     },
33944     leave : function(ev)
33945     {
33946         var dom = ev.getTarget();
33947         //Roo.log(['leave',dom]);
33948         if (!this.currentEl) {
33949             return;
33950         }
33951         
33952         
33953         if (dom != this.currentEl.dom) {
33954             return;
33955         }
33956         var xy = ev.getXY();
33957         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
33958             return;
33959         }
33960         // only activate leave if mouse cursor is outside... bounding box..
33961         
33962         
33963         
33964         
33965         if (this.currentTip) {
33966             this.currentTip.leave();
33967         }
33968         //Roo.log('clear currentEl');
33969         this.currentEl = false;
33970         
33971         
33972     },
33973     alignment : {
33974         'left' : ['r-l', [-2,0], 'right'],
33975         'right' : ['l-r', [2,0], 'left'],
33976         'bottom' : ['t-b', [0,2], 'top'],
33977         'top' : [ 'b-t', [0,-2], 'bottom']
33978     }
33979     
33980 });
33981
33982
33983 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
33984     
33985     
33986     bindEl : false,
33987     
33988     delay : null, // can be { show : 300 , hide: 500}
33989     
33990     timeout : null,
33991     
33992     hoverState : null, //???
33993     
33994     placement : 'bottom', 
33995     
33996     alignment : false,
33997     
33998     getAutoCreate : function(){
33999     
34000         var cfg = {
34001            cls : 'tooltip',   
34002            role : 'tooltip',
34003            cn : [
34004                 {
34005                     cls : 'tooltip-arrow arrow'
34006                 },
34007                 {
34008                     cls : 'tooltip-inner'
34009                 }
34010            ]
34011         };
34012         
34013         return cfg;
34014     },
34015     bind : function(el)
34016     {
34017         this.bindEl = el;
34018     },
34019     
34020     initEvents : function()
34021     {
34022         this.arrowEl = this.el.select('.arrow', true).first();
34023         this.innerEl = this.el.select('.tooltip-inner', true).first();
34024     },
34025     
34026     enter : function () {
34027        
34028         if (this.timeout != null) {
34029             clearTimeout(this.timeout);
34030         }
34031         
34032         this.hoverState = 'in';
34033          //Roo.log("enter - show");
34034         if (!this.delay || !this.delay.show) {
34035             this.show();
34036             return;
34037         }
34038         var _t = this;
34039         this.timeout = setTimeout(function () {
34040             if (_t.hoverState == 'in') {
34041                 _t.show();
34042             }
34043         }, this.delay.show);
34044     },
34045     leave : function()
34046     {
34047         clearTimeout(this.timeout);
34048     
34049         this.hoverState = 'out';
34050          if (!this.delay || !this.delay.hide) {
34051             this.hide();
34052             return;
34053         }
34054        
34055         var _t = this;
34056         this.timeout = setTimeout(function () {
34057             //Roo.log("leave - timeout");
34058             
34059             if (_t.hoverState == 'out') {
34060                 _t.hide();
34061                 Roo.bootstrap.Tooltip.currentEl = false;
34062             }
34063         }, delay);
34064     },
34065     
34066     show : function (msg)
34067     {
34068         if (!this.el) {
34069             this.render(document.body);
34070         }
34071         // set content.
34072         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34073         
34074         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34075         
34076         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34077         
34078         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34079                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34080
34081         if(this.bindEl.attr('tooltip-class')) {
34082             this.el.addClass(this.bindEl.attr('tooltip-class'));
34083         }
34084         
34085         var placement = typeof this.placement == 'function' ?
34086             this.placement.call(this, this.el, on_el) :
34087             this.placement;
34088         
34089         if(this.bindEl.attr('tooltip-placement')) {
34090             placement = this.bindEl.attr('tooltip-placement');
34091         }
34092             
34093         var autoToken = /\s?auto?\s?/i;
34094         var autoPlace = autoToken.test(placement);
34095         if (autoPlace) {
34096             placement = placement.replace(autoToken, '') || 'top';
34097         }
34098         
34099         //this.el.detach()
34100         //this.el.setXY([0,0]);
34101         this.el.show();
34102         //this.el.dom.style.display='block';
34103         
34104         //this.el.appendTo(on_el);
34105         
34106         var p = this.getPosition();
34107         var box = this.el.getBox();
34108         
34109         if (autoPlace) {
34110             // fixme..
34111         }
34112         
34113         var align = this.alignment[placement];
34114         
34115         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34116         
34117         if(placement == 'top' || placement == 'bottom'){
34118             if(xy[0] < 0){
34119                 placement = 'right';
34120             }
34121             
34122             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34123                 placement = 'left';
34124             }
34125             
34126             var scroll = Roo.select('body', true).first().getScroll();
34127             
34128             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34129                 placement = 'top';
34130             }
34131             
34132             align = this.alignment[placement];
34133             
34134             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34135             
34136         }
34137         
34138         var elems = document.getElementsByTagName('div');
34139         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34140         for (var i = 0; i < elems.length; i++) {
34141           var zindex = Number.parseInt(
34142                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34143                 10
34144           );
34145           if (zindex > highest) {
34146             highest = zindex;
34147           }
34148         }
34149         
34150         
34151         
34152         this.el.dom.style.zIndex = highest;
34153         
34154         this.el.alignTo(this.bindEl, align[0],align[1]);
34155         //var arrow = this.el.select('.arrow',true).first();
34156         //arrow.set(align[2], 
34157         
34158         this.el.addClass(placement);
34159         this.el.addClass("bs-tooltip-"+ placement);
34160         
34161         this.el.addClass('in fade show');
34162         
34163         this.hoverState = null;
34164         
34165         if (this.el.hasClass('fade')) {
34166             // fade it?
34167         }
34168         
34169         
34170         
34171         
34172         
34173     },
34174     hide : function()
34175     {
34176          
34177         if (!this.el) {
34178             return;
34179         }
34180         //this.el.setXY([0,0]);
34181         if(this.bindEl.attr('tooltip-class')) {
34182             this.el.removeClass(this.bindEl.attr('tooltip-class'));
34183         }
34184         this.el.removeClass(['show', 'in']);
34185         //this.el.hide();
34186         
34187     }
34188     
34189 });
34190  
34191
34192  /*
34193  * - LGPL
34194  *
34195  * Location Picker
34196  * 
34197  */
34198
34199 /**
34200  * @class Roo.bootstrap.LocationPicker
34201  * @extends Roo.bootstrap.Component
34202  * Bootstrap LocationPicker class
34203  * @cfg {Number} latitude Position when init default 0
34204  * @cfg {Number} longitude Position when init default 0
34205  * @cfg {Number} zoom default 15
34206  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
34207  * @cfg {Boolean} mapTypeControl default false
34208  * @cfg {Boolean} disableDoubleClickZoom default false
34209  * @cfg {Boolean} scrollwheel default true
34210  * @cfg {Boolean} streetViewControl default false
34211  * @cfg {Number} radius default 0
34212  * @cfg {String} locationName
34213  * @cfg {Boolean} draggable default true
34214  * @cfg {Boolean} enableAutocomplete default false
34215  * @cfg {Boolean} enableReverseGeocode default true
34216  * @cfg {String} markerTitle
34217  * 
34218  * @constructor
34219  * Create a new LocationPicker
34220  * @param {Object} config The config object
34221  */
34222
34223
34224 Roo.bootstrap.LocationPicker = function(config){
34225     
34226     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
34227     
34228     this.addEvents({
34229         /**
34230          * @event initial
34231          * Fires when the picker initialized.
34232          * @param {Roo.bootstrap.LocationPicker} this
34233          * @param {Google Location} location
34234          */
34235         initial : true,
34236         /**
34237          * @event positionchanged
34238          * Fires when the picker position changed.
34239          * @param {Roo.bootstrap.LocationPicker} this
34240          * @param {Google Location} location
34241          */
34242         positionchanged : true,
34243         /**
34244          * @event resize
34245          * Fires when the map resize.
34246          * @param {Roo.bootstrap.LocationPicker} this
34247          */
34248         resize : true,
34249         /**
34250          * @event show
34251          * Fires when the map show.
34252          * @param {Roo.bootstrap.LocationPicker} this
34253          */
34254         show : true,
34255         /**
34256          * @event hide
34257          * Fires when the map hide.
34258          * @param {Roo.bootstrap.LocationPicker} this
34259          */
34260         hide : true,
34261         /**
34262          * @event mapClick
34263          * Fires when click the map.
34264          * @param {Roo.bootstrap.LocationPicker} this
34265          * @param {Map event} e
34266          */
34267         mapClick : true,
34268         /**
34269          * @event mapRightClick
34270          * Fires when right click the map.
34271          * @param {Roo.bootstrap.LocationPicker} this
34272          * @param {Map event} e
34273          */
34274         mapRightClick : true,
34275         /**
34276          * @event markerClick
34277          * Fires when click the marker.
34278          * @param {Roo.bootstrap.LocationPicker} this
34279          * @param {Map event} e
34280          */
34281         markerClick : true,
34282         /**
34283          * @event markerRightClick
34284          * Fires when right click the marker.
34285          * @param {Roo.bootstrap.LocationPicker} this
34286          * @param {Map event} e
34287          */
34288         markerRightClick : true,
34289         /**
34290          * @event OverlayViewDraw
34291          * Fires when OverlayView Draw
34292          * @param {Roo.bootstrap.LocationPicker} this
34293          */
34294         OverlayViewDraw : true,
34295         /**
34296          * @event OverlayViewOnAdd
34297          * Fires when OverlayView Draw
34298          * @param {Roo.bootstrap.LocationPicker} this
34299          */
34300         OverlayViewOnAdd : true,
34301         /**
34302          * @event OverlayViewOnRemove
34303          * Fires when OverlayView Draw
34304          * @param {Roo.bootstrap.LocationPicker} this
34305          */
34306         OverlayViewOnRemove : true,
34307         /**
34308          * @event OverlayViewShow
34309          * Fires when OverlayView Draw
34310          * @param {Roo.bootstrap.LocationPicker} this
34311          * @param {Pixel} cpx
34312          */
34313         OverlayViewShow : true,
34314         /**
34315          * @event OverlayViewHide
34316          * Fires when OverlayView Draw
34317          * @param {Roo.bootstrap.LocationPicker} this
34318          */
34319         OverlayViewHide : true,
34320         /**
34321          * @event loadexception
34322          * Fires when load google lib failed.
34323          * @param {Roo.bootstrap.LocationPicker} this
34324          */
34325         loadexception : true
34326     });
34327         
34328 };
34329
34330 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
34331     
34332     gMapContext: false,
34333     
34334     latitude: 0,
34335     longitude: 0,
34336     zoom: 15,
34337     mapTypeId: false,
34338     mapTypeControl: false,
34339     disableDoubleClickZoom: false,
34340     scrollwheel: true,
34341     streetViewControl: false,
34342     radius: 0,
34343     locationName: '',
34344     draggable: true,
34345     enableAutocomplete: false,
34346     enableReverseGeocode: true,
34347     markerTitle: '',
34348     
34349     getAutoCreate: function()
34350     {
34351
34352         var cfg = {
34353             tag: 'div',
34354             cls: 'roo-location-picker'
34355         };
34356         
34357         return cfg
34358     },
34359     
34360     initEvents: function(ct, position)
34361     {       
34362         if(!this.el.getWidth() || this.isApplied()){
34363             return;
34364         }
34365         
34366         this.el.setVisibilityMode(Roo.Element.DISPLAY);
34367         
34368         this.initial();
34369     },
34370     
34371     initial: function()
34372     {
34373         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
34374             this.fireEvent('loadexception', this);
34375             return;
34376         }
34377         
34378         if(!this.mapTypeId){
34379             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
34380         }
34381         
34382         this.gMapContext = this.GMapContext();
34383         
34384         this.initOverlayView();
34385         
34386         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
34387         
34388         var _this = this;
34389                 
34390         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
34391             _this.setPosition(_this.gMapContext.marker.position);
34392         });
34393         
34394         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
34395             _this.fireEvent('mapClick', this, event);
34396             
34397         });
34398
34399         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
34400             _this.fireEvent('mapRightClick', this, event);
34401             
34402         });
34403         
34404         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
34405             _this.fireEvent('markerClick', this, event);
34406             
34407         });
34408
34409         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
34410             _this.fireEvent('markerRightClick', this, event);
34411             
34412         });
34413         
34414         this.setPosition(this.gMapContext.location);
34415         
34416         this.fireEvent('initial', this, this.gMapContext.location);
34417     },
34418     
34419     initOverlayView: function()
34420     {
34421         var _this = this;
34422         
34423         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
34424             
34425             draw: function()
34426             {
34427                 _this.fireEvent('OverlayViewDraw', _this);
34428             },
34429             
34430             onAdd: function()
34431             {
34432                 _this.fireEvent('OverlayViewOnAdd', _this);
34433             },
34434             
34435             onRemove: function()
34436             {
34437                 _this.fireEvent('OverlayViewOnRemove', _this);
34438             },
34439             
34440             show: function(cpx)
34441             {
34442                 _this.fireEvent('OverlayViewShow', _this, cpx);
34443             },
34444             
34445             hide: function()
34446             {
34447                 _this.fireEvent('OverlayViewHide', _this);
34448             }
34449             
34450         });
34451     },
34452     
34453     fromLatLngToContainerPixel: function(event)
34454     {
34455         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
34456     },
34457     
34458     isApplied: function() 
34459     {
34460         return this.getGmapContext() == false ? false : true;
34461     },
34462     
34463     getGmapContext: function() 
34464     {
34465         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
34466     },
34467     
34468     GMapContext: function() 
34469     {
34470         var position = new google.maps.LatLng(this.latitude, this.longitude);
34471         
34472         var _map = new google.maps.Map(this.el.dom, {
34473             center: position,
34474             zoom: this.zoom,
34475             mapTypeId: this.mapTypeId,
34476             mapTypeControl: this.mapTypeControl,
34477             disableDoubleClickZoom: this.disableDoubleClickZoom,
34478             scrollwheel: this.scrollwheel,
34479             streetViewControl: this.streetViewControl,
34480             locationName: this.locationName,
34481             draggable: this.draggable,
34482             enableAutocomplete: this.enableAutocomplete,
34483             enableReverseGeocode: this.enableReverseGeocode
34484         });
34485         
34486         var _marker = new google.maps.Marker({
34487             position: position,
34488             map: _map,
34489             title: this.markerTitle,
34490             draggable: this.draggable
34491         });
34492         
34493         return {
34494             map: _map,
34495             marker: _marker,
34496             circle: null,
34497             location: position,
34498             radius: this.radius,
34499             locationName: this.locationName,
34500             addressComponents: {
34501                 formatted_address: null,
34502                 addressLine1: null,
34503                 addressLine2: null,
34504                 streetName: null,
34505                 streetNumber: null,
34506                 city: null,
34507                 district: null,
34508                 state: null,
34509                 stateOrProvince: null
34510             },
34511             settings: this,
34512             domContainer: this.el.dom,
34513             geodecoder: new google.maps.Geocoder()
34514         };
34515     },
34516     
34517     drawCircle: function(center, radius, options) 
34518     {
34519         if (this.gMapContext.circle != null) {
34520             this.gMapContext.circle.setMap(null);
34521         }
34522         if (radius > 0) {
34523             radius *= 1;
34524             options = Roo.apply({}, options, {
34525                 strokeColor: "#0000FF",
34526                 strokeOpacity: .35,
34527                 strokeWeight: 2,
34528                 fillColor: "#0000FF",
34529                 fillOpacity: .2
34530             });
34531             
34532             options.map = this.gMapContext.map;
34533             options.radius = radius;
34534             options.center = center;
34535             this.gMapContext.circle = new google.maps.Circle(options);
34536             return this.gMapContext.circle;
34537         }
34538         
34539         return null;
34540     },
34541     
34542     setPosition: function(location) 
34543     {
34544         this.gMapContext.location = location;
34545         this.gMapContext.marker.setPosition(location);
34546         this.gMapContext.map.panTo(location);
34547         this.drawCircle(location, this.gMapContext.radius, {});
34548         
34549         var _this = this;
34550         
34551         if (this.gMapContext.settings.enableReverseGeocode) {
34552             this.gMapContext.geodecoder.geocode({
34553                 latLng: this.gMapContext.location
34554             }, function(results, status) {
34555                 
34556                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
34557                     _this.gMapContext.locationName = results[0].formatted_address;
34558                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
34559                     
34560                     _this.fireEvent('positionchanged', this, location);
34561                 }
34562             });
34563             
34564             return;
34565         }
34566         
34567         this.fireEvent('positionchanged', this, location);
34568     },
34569     
34570     resize: function()
34571     {
34572         google.maps.event.trigger(this.gMapContext.map, "resize");
34573         
34574         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
34575         
34576         this.fireEvent('resize', this);
34577     },
34578     
34579     setPositionByLatLng: function(latitude, longitude)
34580     {
34581         this.setPosition(new google.maps.LatLng(latitude, longitude));
34582     },
34583     
34584     getCurrentPosition: function() 
34585     {
34586         return {
34587             latitude: this.gMapContext.location.lat(),
34588             longitude: this.gMapContext.location.lng()
34589         };
34590     },
34591     
34592     getAddressName: function() 
34593     {
34594         return this.gMapContext.locationName;
34595     },
34596     
34597     getAddressComponents: function() 
34598     {
34599         return this.gMapContext.addressComponents;
34600     },
34601     
34602     address_component_from_google_geocode: function(address_components) 
34603     {
34604         var result = {};
34605         
34606         for (var i = 0; i < address_components.length; i++) {
34607             var component = address_components[i];
34608             if (component.types.indexOf("postal_code") >= 0) {
34609                 result.postalCode = component.short_name;
34610             } else if (component.types.indexOf("street_number") >= 0) {
34611                 result.streetNumber = component.short_name;
34612             } else if (component.types.indexOf("route") >= 0) {
34613                 result.streetName = component.short_name;
34614             } else if (component.types.indexOf("neighborhood") >= 0) {
34615                 result.city = component.short_name;
34616             } else if (component.types.indexOf("locality") >= 0) {
34617                 result.city = component.short_name;
34618             } else if (component.types.indexOf("sublocality") >= 0) {
34619                 result.district = component.short_name;
34620             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
34621                 result.stateOrProvince = component.short_name;
34622             } else if (component.types.indexOf("country") >= 0) {
34623                 result.country = component.short_name;
34624             }
34625         }
34626         
34627         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
34628         result.addressLine2 = "";
34629         return result;
34630     },
34631     
34632     setZoomLevel: function(zoom)
34633     {
34634         this.gMapContext.map.setZoom(zoom);
34635     },
34636     
34637     show: function()
34638     {
34639         if(!this.el){
34640             return;
34641         }
34642         
34643         this.el.show();
34644         
34645         this.resize();
34646         
34647         this.fireEvent('show', this);
34648     },
34649     
34650     hide: function()
34651     {
34652         if(!this.el){
34653             return;
34654         }
34655         
34656         this.el.hide();
34657         
34658         this.fireEvent('hide', this);
34659     }
34660     
34661 });
34662
34663 Roo.apply(Roo.bootstrap.LocationPicker, {
34664     
34665     OverlayView : function(map, options)
34666     {
34667         options = options || {};
34668         
34669         this.setMap(map);
34670     }
34671     
34672     
34673 });/**
34674  * @class Roo.bootstrap.Alert
34675  * @extends Roo.bootstrap.Component
34676  * Bootstrap Alert class - shows an alert area box
34677  * eg
34678  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
34679   Enter a valid email address
34680 </div>
34681  * @licence LGPL
34682  * @cfg {String} title The title of alert
34683  * @cfg {String} html The content of alert
34684  * @cfg {String} weight (success|info|warning|danger) Weight of the message
34685  * @cfg {String} fa font-awesomeicon
34686  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
34687  * @cfg {Boolean} close true to show a x closer
34688  * 
34689  * 
34690  * @constructor
34691  * Create a new alert
34692  * @param {Object} config The config object
34693  */
34694
34695
34696 Roo.bootstrap.Alert = function(config){
34697     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
34698     
34699 };
34700
34701 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
34702     
34703     title: '',
34704     html: '',
34705     weight: false,
34706     fa: false,
34707     faicon: false, // BC
34708     close : false,
34709     
34710     
34711     getAutoCreate : function()
34712     {
34713         
34714         var cfg = {
34715             tag : 'div',
34716             cls : 'alert',
34717             cn : [
34718                 {
34719                     tag: 'button',
34720                     type :  "button",
34721                     cls: "close",
34722                     html : '×',
34723                     style : this.close ? '' : 'display:none'
34724                 },
34725                 {
34726                     tag : 'i',
34727                     cls : 'roo-alert-icon'
34728                     
34729                 },
34730                 {
34731                     tag : 'b',
34732                     cls : 'roo-alert-title',
34733                     html : this.title
34734                 },
34735                 {
34736                     tag : 'span',
34737                     cls : 'roo-alert-text',
34738                     html : this.html
34739                 }
34740             ]
34741         };
34742         
34743         if(this.faicon){
34744             cfg.cn[0].cls += ' fa ' + this.faicon;
34745         }
34746         if(this.fa){
34747             cfg.cn[0].cls += ' fa ' + this.fa;
34748         }
34749         
34750         if(this.weight){
34751             cfg.cls += ' alert-' + this.weight;
34752         }
34753         
34754         return cfg;
34755     },
34756     
34757     initEvents: function() 
34758     {
34759         this.el.setVisibilityMode(Roo.Element.DISPLAY);
34760         this.titleEl =  this.el.select('.roo-alert-title',true).first();
34761         this.iconEl = this.el.select('.roo-alert-icon',true).first();
34762         this.htmlEl = this.el.select('.roo-alert-text',true).first();
34763         if (this.seconds > 0) {
34764             this.hide.defer(this.seconds, this);
34765         }
34766     },
34767     /**
34768      * Set the Title Message HTML
34769      * @param {String} html
34770      */
34771     setTitle : function(str)
34772     {
34773         this.titleEl.dom.innerHTML = str;
34774     },
34775      
34776      /**
34777      * Set the Body Message HTML
34778      * @param {String} html
34779      */
34780     setHtml : function(str)
34781     {
34782         this.htmlEl.dom.innerHTML = str;
34783     },
34784     /**
34785      * Set the Weight of the alert
34786      * @param {String} (success|info|warning|danger) weight
34787      */
34788     
34789     setWeight : function(weight)
34790     {
34791         if(this.weight){
34792             this.el.removeClass('alert-' + this.weight);
34793         }
34794         
34795         this.weight = weight;
34796         
34797         this.el.addClass('alert-' + this.weight);
34798     },
34799       /**
34800      * Set the Icon of the alert
34801      * @param {String} see fontawsome names (name without the 'fa-' bit)
34802      */
34803     setIcon : function(icon)
34804     {
34805         if(this.faicon){
34806             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
34807         }
34808         
34809         this.faicon = icon;
34810         
34811         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
34812     },
34813     /**
34814      * Hide the Alert
34815      */
34816     hide: function() 
34817     {
34818         this.el.hide();   
34819     },
34820     /**
34821      * Show the Alert
34822      */
34823     show: function() 
34824     {  
34825         this.el.show();   
34826     }
34827     
34828 });
34829
34830  
34831 /*
34832 * Licence: LGPL
34833 */
34834
34835 /**
34836  * @class Roo.bootstrap.UploadCropbox
34837  * @extends Roo.bootstrap.Component
34838  * Bootstrap UploadCropbox class
34839  * @cfg {String} emptyText show when image has been loaded
34840  * @cfg {String} rotateNotify show when image too small to rotate
34841  * @cfg {Number} errorTimeout default 3000
34842  * @cfg {Number} minWidth default 300
34843  * @cfg {Number} minHeight default 300
34844  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
34845  * @cfg {Boolean} isDocument (true|false) default false
34846  * @cfg {String} url action url
34847  * @cfg {String} paramName default 'imageUpload'
34848  * @cfg {String} method default POST
34849  * @cfg {Boolean} loadMask (true|false) default true
34850  * @cfg {Boolean} loadingText default 'Loading...'
34851  * 
34852  * @constructor
34853  * Create a new UploadCropbox
34854  * @param {Object} config The config object
34855  */
34856
34857 Roo.bootstrap.UploadCropbox = function(config){
34858     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
34859     
34860     this.addEvents({
34861         /**
34862          * @event beforeselectfile
34863          * Fire before select file
34864          * @param {Roo.bootstrap.UploadCropbox} this
34865          */
34866         "beforeselectfile" : true,
34867         /**
34868          * @event initial
34869          * Fire after initEvent
34870          * @param {Roo.bootstrap.UploadCropbox} this
34871          */
34872         "initial" : true,
34873         /**
34874          * @event crop
34875          * Fire after initEvent
34876          * @param {Roo.bootstrap.UploadCropbox} this
34877          * @param {String} data
34878          */
34879         "crop" : true,
34880         /**
34881          * @event prepare
34882          * Fire when preparing the file data
34883          * @param {Roo.bootstrap.UploadCropbox} this
34884          * @param {Object} file
34885          */
34886         "prepare" : true,
34887         /**
34888          * @event exception
34889          * Fire when get exception
34890          * @param {Roo.bootstrap.UploadCropbox} this
34891          * @param {XMLHttpRequest} xhr
34892          */
34893         "exception" : true,
34894         /**
34895          * @event beforeloadcanvas
34896          * Fire before load the canvas
34897          * @param {Roo.bootstrap.UploadCropbox} this
34898          * @param {String} src
34899          */
34900         "beforeloadcanvas" : true,
34901         /**
34902          * @event trash
34903          * Fire when trash image
34904          * @param {Roo.bootstrap.UploadCropbox} this
34905          */
34906         "trash" : true,
34907         /**
34908          * @event download
34909          * Fire when download the image
34910          * @param {Roo.bootstrap.UploadCropbox} this
34911          */
34912         "download" : true,
34913         /**
34914          * @event footerbuttonclick
34915          * Fire when footerbuttonclick
34916          * @param {Roo.bootstrap.UploadCropbox} this
34917          * @param {String} type
34918          */
34919         "footerbuttonclick" : true,
34920         /**
34921          * @event resize
34922          * Fire when resize
34923          * @param {Roo.bootstrap.UploadCropbox} this
34924          */
34925         "resize" : true,
34926         /**
34927          * @event rotate
34928          * Fire when rotate the image
34929          * @param {Roo.bootstrap.UploadCropbox} this
34930          * @param {String} pos
34931          */
34932         "rotate" : true,
34933         /**
34934          * @event inspect
34935          * Fire when inspect the file
34936          * @param {Roo.bootstrap.UploadCropbox} this
34937          * @param {Object} file
34938          */
34939         "inspect" : true,
34940         /**
34941          * @event upload
34942          * Fire when xhr upload the file
34943          * @param {Roo.bootstrap.UploadCropbox} this
34944          * @param {Object} data
34945          */
34946         "upload" : true,
34947         /**
34948          * @event arrange
34949          * Fire when arrange the file data
34950          * @param {Roo.bootstrap.UploadCropbox} this
34951          * @param {Object} formData
34952          */
34953         "arrange" : true
34954     });
34955     
34956     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
34957 };
34958
34959 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
34960     
34961     emptyText : 'Click to upload image',
34962     rotateNotify : 'Image is too small to rotate',
34963     errorTimeout : 3000,
34964     scale : 0,
34965     baseScale : 1,
34966     rotate : 0,
34967     dragable : false,
34968     pinching : false,
34969     mouseX : 0,
34970     mouseY : 0,
34971     cropData : false,
34972     minWidth : 300,
34973     minHeight : 300,
34974     file : false,
34975     exif : {},
34976     baseRotate : 1,
34977     cropType : 'image/jpeg',
34978     buttons : false,
34979     canvasLoaded : false,
34980     isDocument : false,
34981     method : 'POST',
34982     paramName : 'imageUpload',
34983     loadMask : true,
34984     loadingText : 'Loading...',
34985     maskEl : false,
34986     
34987     getAutoCreate : function()
34988     {
34989         var cfg = {
34990             tag : 'div',
34991             cls : 'roo-upload-cropbox',
34992             cn : [
34993                 {
34994                     tag : 'input',
34995                     cls : 'roo-upload-cropbox-selector',
34996                     type : 'file'
34997                 },
34998                 {
34999                     tag : 'div',
35000                     cls : 'roo-upload-cropbox-body',
35001                     style : 'cursor:pointer',
35002                     cn : [
35003                         {
35004                             tag : 'div',
35005                             cls : 'roo-upload-cropbox-preview'
35006                         },
35007                         {
35008                             tag : 'div',
35009                             cls : 'roo-upload-cropbox-thumb'
35010                         },
35011                         {
35012                             tag : 'div',
35013                             cls : 'roo-upload-cropbox-empty-notify',
35014                             html : this.emptyText
35015                         },
35016                         {
35017                             tag : 'div',
35018                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35019                             html : this.rotateNotify
35020                         }
35021                     ]
35022                 },
35023                 {
35024                     tag : 'div',
35025                     cls : 'roo-upload-cropbox-footer',
35026                     cn : {
35027                         tag : 'div',
35028                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35029                         cn : []
35030                     }
35031                 }
35032             ]
35033         };
35034         
35035         return cfg;
35036     },
35037     
35038     onRender : function(ct, position)
35039     {
35040         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35041         
35042         if (this.buttons.length) {
35043             
35044             Roo.each(this.buttons, function(bb) {
35045                 
35046                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35047                 
35048                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35049                 
35050             }, this);
35051         }
35052         
35053         if(this.loadMask){
35054             this.maskEl = this.el;
35055         }
35056     },
35057     
35058     initEvents : function()
35059     {
35060         this.urlAPI = (window.createObjectURL && window) || 
35061                                 (window.URL && URL.revokeObjectURL && URL) || 
35062                                 (window.webkitURL && webkitURL);
35063                         
35064         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35065         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35066         
35067         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35068         this.selectorEl.hide();
35069         
35070         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35071         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35072         
35073         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35074         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35075         this.thumbEl.hide();
35076         
35077         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35078         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35079         
35080         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35081         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35082         this.errorEl.hide();
35083         
35084         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35085         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35086         this.footerEl.hide();
35087         
35088         this.setThumbBoxSize();
35089         
35090         this.bind();
35091         
35092         this.resize();
35093         
35094         this.fireEvent('initial', this);
35095     },
35096
35097     bind : function()
35098     {
35099         var _this = this;
35100         
35101         window.addEventListener("resize", function() { _this.resize(); } );
35102         
35103         this.bodyEl.on('click', this.beforeSelectFile, this);
35104         
35105         if(Roo.isTouch){
35106             this.bodyEl.on('touchstart', this.onTouchStart, this);
35107             this.bodyEl.on('touchmove', this.onTouchMove, this);
35108             this.bodyEl.on('touchend', this.onTouchEnd, this);
35109         }
35110         
35111         if(!Roo.isTouch){
35112             this.bodyEl.on('mousedown', this.onMouseDown, this);
35113             this.bodyEl.on('mousemove', this.onMouseMove, this);
35114             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35115             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35116             Roo.get(document).on('mouseup', this.onMouseUp, this);
35117         }
35118         
35119         this.selectorEl.on('change', this.onFileSelected, this);
35120     },
35121     
35122     reset : function()
35123     {    
35124         this.scale = 0;
35125         this.baseScale = 1;
35126         this.rotate = 0;
35127         this.baseRotate = 1;
35128         this.dragable = false;
35129         this.pinching = false;
35130         this.mouseX = 0;
35131         this.mouseY = 0;
35132         this.cropData = false;
35133         this.notifyEl.dom.innerHTML = this.emptyText;
35134         
35135         this.selectorEl.dom.value = '';
35136         
35137     },
35138     
35139     resize : function()
35140     {
35141         if(this.fireEvent('resize', this) != false){
35142             this.setThumbBoxPosition();
35143             this.setCanvasPosition();
35144         }
35145     },
35146     
35147     onFooterButtonClick : function(e, el, o, type)
35148     {
35149         switch (type) {
35150             case 'rotate-left' :
35151                 this.onRotateLeft(e);
35152                 break;
35153             case 'rotate-right' :
35154                 this.onRotateRight(e);
35155                 break;
35156             case 'picture' :
35157                 this.beforeSelectFile(e);
35158                 break;
35159             case 'trash' :
35160                 this.trash(e);
35161                 break;
35162             case 'crop' :
35163                 this.crop(e);
35164                 break;
35165             case 'download' :
35166                 this.download(e);
35167                 break;
35168             default :
35169                 break;
35170         }
35171         
35172         this.fireEvent('footerbuttonclick', this, type);
35173     },
35174     
35175     beforeSelectFile : function(e)
35176     {
35177         e.preventDefault();
35178         
35179         if(this.fireEvent('beforeselectfile', this) != false){
35180             this.selectorEl.dom.click();
35181         }
35182     },
35183     
35184     onFileSelected : function(e)
35185     {
35186         e.preventDefault();
35187         
35188         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35189             return;
35190         }
35191         
35192         var file = this.selectorEl.dom.files[0];
35193         
35194         if(this.fireEvent('inspect', this, file) != false){
35195             this.prepare(file);
35196         }
35197         
35198     },
35199     
35200     trash : function(e)
35201     {
35202         this.fireEvent('trash', this);
35203     },
35204     
35205     download : function(e)
35206     {
35207         this.fireEvent('download', this);
35208     },
35209     
35210     loadCanvas : function(src)
35211     {   
35212         if(this.fireEvent('beforeloadcanvas', this, src) != false){
35213             
35214             this.reset();
35215             
35216             this.imageEl = document.createElement('img');
35217             
35218             var _this = this;
35219             
35220             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
35221             
35222             this.imageEl.src = src;
35223         }
35224     },
35225     
35226     onLoadCanvas : function()
35227     {   
35228         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
35229         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
35230         
35231         this.bodyEl.un('click', this.beforeSelectFile, this);
35232         
35233         this.notifyEl.hide();
35234         this.thumbEl.show();
35235         this.footerEl.show();
35236         
35237         this.baseRotateLevel();
35238         
35239         if(this.isDocument){
35240             this.setThumbBoxSize();
35241         }
35242         
35243         this.setThumbBoxPosition();
35244         
35245         this.baseScaleLevel();
35246         
35247         this.draw();
35248         
35249         this.resize();
35250         
35251         this.canvasLoaded = true;
35252         
35253         if(this.loadMask){
35254             this.maskEl.unmask();
35255         }
35256         
35257     },
35258     
35259     setCanvasPosition : function()
35260     {   
35261         if(!this.canvasEl){
35262             return;
35263         }
35264         
35265         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
35266         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
35267         
35268         this.previewEl.setLeft(pw);
35269         this.previewEl.setTop(ph);
35270         
35271     },
35272     
35273     onMouseDown : function(e)
35274     {   
35275         e.stopEvent();
35276         
35277         this.dragable = true;
35278         this.pinching = false;
35279         
35280         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
35281             this.dragable = false;
35282             return;
35283         }
35284         
35285         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35286         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35287         
35288     },
35289     
35290     onMouseMove : function(e)
35291     {   
35292         e.stopEvent();
35293         
35294         if(!this.canvasLoaded){
35295             return;
35296         }
35297         
35298         if (!this.dragable){
35299             return;
35300         }
35301         
35302         var minX = Math.ceil(this.thumbEl.getLeft(true));
35303         var minY = Math.ceil(this.thumbEl.getTop(true));
35304         
35305         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
35306         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
35307         
35308         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35309         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35310         
35311         x = x - this.mouseX;
35312         y = y - this.mouseY;
35313         
35314         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
35315         var bgY = Math.ceil(y + this.previewEl.getTop(true));
35316         
35317         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
35318         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
35319         
35320         this.previewEl.setLeft(bgX);
35321         this.previewEl.setTop(bgY);
35322         
35323         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35324         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35325     },
35326     
35327     onMouseUp : function(e)
35328     {   
35329         e.stopEvent();
35330         
35331         this.dragable = false;
35332     },
35333     
35334     onMouseWheel : function(e)
35335     {   
35336         e.stopEvent();
35337         
35338         this.startScale = this.scale;
35339         
35340         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
35341         
35342         if(!this.zoomable()){
35343             this.scale = this.startScale;
35344             return;
35345         }
35346         
35347         this.draw();
35348         
35349         return;
35350     },
35351     
35352     zoomable : function()
35353     {
35354         var minScale = this.thumbEl.getWidth() / this.minWidth;
35355         
35356         if(this.minWidth < this.minHeight){
35357             minScale = this.thumbEl.getHeight() / this.minHeight;
35358         }
35359         
35360         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
35361         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
35362         
35363         if(
35364                 this.isDocument &&
35365                 (this.rotate == 0 || this.rotate == 180) && 
35366                 (
35367                     width > this.imageEl.OriginWidth || 
35368                     height > this.imageEl.OriginHeight ||
35369                     (width < this.minWidth && height < this.minHeight)
35370                 )
35371         ){
35372             return false;
35373         }
35374         
35375         if(
35376                 this.isDocument &&
35377                 (this.rotate == 90 || this.rotate == 270) && 
35378                 (
35379                     width > this.imageEl.OriginWidth || 
35380                     height > this.imageEl.OriginHeight ||
35381                     (width < this.minHeight && height < this.minWidth)
35382                 )
35383         ){
35384             return false;
35385         }
35386         
35387         if(
35388                 !this.isDocument &&
35389                 (this.rotate == 0 || this.rotate == 180) && 
35390                 (
35391                     width < this.minWidth || 
35392                     width > this.imageEl.OriginWidth || 
35393                     height < this.minHeight || 
35394                     height > this.imageEl.OriginHeight
35395                 )
35396         ){
35397             return false;
35398         }
35399         
35400         if(
35401                 !this.isDocument &&
35402                 (this.rotate == 90 || this.rotate == 270) && 
35403                 (
35404                     width < this.minHeight || 
35405                     width > this.imageEl.OriginWidth || 
35406                     height < this.minWidth || 
35407                     height > this.imageEl.OriginHeight
35408                 )
35409         ){
35410             return false;
35411         }
35412         
35413         return true;
35414         
35415     },
35416     
35417     onRotateLeft : function(e)
35418     {   
35419         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35420             
35421             var minScale = this.thumbEl.getWidth() / this.minWidth;
35422             
35423             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35424             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35425             
35426             this.startScale = this.scale;
35427             
35428             while (this.getScaleLevel() < minScale){
35429             
35430                 this.scale = this.scale + 1;
35431                 
35432                 if(!this.zoomable()){
35433                     break;
35434                 }
35435                 
35436                 if(
35437                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35438                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35439                 ){
35440                     continue;
35441                 }
35442                 
35443                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35444
35445                 this.draw();
35446                 
35447                 return;
35448             }
35449             
35450             this.scale = this.startScale;
35451             
35452             this.onRotateFail();
35453             
35454             return false;
35455         }
35456         
35457         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35458
35459         if(this.isDocument){
35460             this.setThumbBoxSize();
35461             this.setThumbBoxPosition();
35462             this.setCanvasPosition();
35463         }
35464         
35465         this.draw();
35466         
35467         this.fireEvent('rotate', this, 'left');
35468         
35469     },
35470     
35471     onRotateRight : function(e)
35472     {
35473         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35474             
35475             var minScale = this.thumbEl.getWidth() / this.minWidth;
35476         
35477             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35478             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35479             
35480             this.startScale = this.scale;
35481             
35482             while (this.getScaleLevel() < minScale){
35483             
35484                 this.scale = this.scale + 1;
35485                 
35486                 if(!this.zoomable()){
35487                     break;
35488                 }
35489                 
35490                 if(
35491                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35492                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35493                 ){
35494                     continue;
35495                 }
35496                 
35497                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35498
35499                 this.draw();
35500                 
35501                 return;
35502             }
35503             
35504             this.scale = this.startScale;
35505             
35506             this.onRotateFail();
35507             
35508             return false;
35509         }
35510         
35511         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35512
35513         if(this.isDocument){
35514             this.setThumbBoxSize();
35515             this.setThumbBoxPosition();
35516             this.setCanvasPosition();
35517         }
35518         
35519         this.draw();
35520         
35521         this.fireEvent('rotate', this, 'right');
35522     },
35523     
35524     onRotateFail : function()
35525     {
35526         this.errorEl.show(true);
35527         
35528         var _this = this;
35529         
35530         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
35531     },
35532     
35533     draw : function()
35534     {
35535         this.previewEl.dom.innerHTML = '';
35536         
35537         var canvasEl = document.createElement("canvas");
35538         
35539         var contextEl = canvasEl.getContext("2d");
35540         
35541         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35542         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35543         var center = this.imageEl.OriginWidth / 2;
35544         
35545         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
35546             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35547             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35548             center = this.imageEl.OriginHeight / 2;
35549         }
35550         
35551         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
35552         
35553         contextEl.translate(center, center);
35554         contextEl.rotate(this.rotate * Math.PI / 180);
35555
35556         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35557         
35558         this.canvasEl = document.createElement("canvas");
35559         
35560         this.contextEl = this.canvasEl.getContext("2d");
35561         
35562         switch (this.rotate) {
35563             case 0 :
35564                 
35565                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35566                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35567                 
35568                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35569                 
35570                 break;
35571             case 90 : 
35572                 
35573                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35574                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35575                 
35576                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35577                     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);
35578                     break;
35579                 }
35580                 
35581                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35582                 
35583                 break;
35584             case 180 :
35585                 
35586                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35587                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35588                 
35589                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35590                     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);
35591                     break;
35592                 }
35593                 
35594                 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);
35595                 
35596                 break;
35597             case 270 :
35598                 
35599                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35600                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35601         
35602                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35603                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35604                     break;
35605                 }
35606                 
35607                 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);
35608                 
35609                 break;
35610             default : 
35611                 break;
35612         }
35613         
35614         this.previewEl.appendChild(this.canvasEl);
35615         
35616         this.setCanvasPosition();
35617     },
35618     
35619     crop : function()
35620     {
35621         if(!this.canvasLoaded){
35622             return;
35623         }
35624         
35625         var imageCanvas = document.createElement("canvas");
35626         
35627         var imageContext = imageCanvas.getContext("2d");
35628         
35629         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
35630         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
35631         
35632         var center = imageCanvas.width / 2;
35633         
35634         imageContext.translate(center, center);
35635         
35636         imageContext.rotate(this.rotate * Math.PI / 180);
35637         
35638         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35639         
35640         var canvas = document.createElement("canvas");
35641         
35642         var context = canvas.getContext("2d");
35643                 
35644         canvas.width = this.minWidth;
35645         canvas.height = this.minHeight;
35646
35647         switch (this.rotate) {
35648             case 0 :
35649                 
35650                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
35651                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
35652                 
35653                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35654                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35655                 
35656                 var targetWidth = this.minWidth - 2 * x;
35657                 var targetHeight = this.minHeight - 2 * y;
35658                 
35659                 var scale = 1;
35660                 
35661                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35662                     scale = targetWidth / width;
35663                 }
35664                 
35665                 if(x > 0 && y == 0){
35666                     scale = targetHeight / height;
35667                 }
35668                 
35669                 if(x > 0 && y > 0){
35670                     scale = targetWidth / width;
35671                     
35672                     if(width < height){
35673                         scale = targetHeight / height;
35674                     }
35675                 }
35676                 
35677                 context.scale(scale, scale);
35678                 
35679                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35680                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35681
35682                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35683                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35684
35685                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35686                 
35687                 break;
35688             case 90 : 
35689                 
35690                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
35691                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
35692                 
35693                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35694                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35695                 
35696                 var targetWidth = this.minWidth - 2 * x;
35697                 var targetHeight = this.minHeight - 2 * y;
35698                 
35699                 var scale = 1;
35700                 
35701                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35702                     scale = targetWidth / width;
35703                 }
35704                 
35705                 if(x > 0 && y == 0){
35706                     scale = targetHeight / height;
35707                 }
35708                 
35709                 if(x > 0 && y > 0){
35710                     scale = targetWidth / width;
35711                     
35712                     if(width < height){
35713                         scale = targetHeight / height;
35714                     }
35715                 }
35716                 
35717                 context.scale(scale, scale);
35718                 
35719                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35720                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35721
35722                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35723                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35724                 
35725                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
35726                 
35727                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35728                 
35729                 break;
35730             case 180 :
35731                 
35732                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
35733                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
35734                 
35735                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35736                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35737                 
35738                 var targetWidth = this.minWidth - 2 * x;
35739                 var targetHeight = this.minHeight - 2 * y;
35740                 
35741                 var scale = 1;
35742                 
35743                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35744                     scale = targetWidth / width;
35745                 }
35746                 
35747                 if(x > 0 && y == 0){
35748                     scale = targetHeight / height;
35749                 }
35750                 
35751                 if(x > 0 && y > 0){
35752                     scale = targetWidth / width;
35753                     
35754                     if(width < height){
35755                         scale = targetHeight / height;
35756                     }
35757                 }
35758                 
35759                 context.scale(scale, scale);
35760                 
35761                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35762                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35763
35764                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35765                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35766
35767                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35768                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
35769                 
35770                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35771                 
35772                 break;
35773             case 270 :
35774                 
35775                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
35776                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
35777                 
35778                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35779                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35780                 
35781                 var targetWidth = this.minWidth - 2 * x;
35782                 var targetHeight = this.minHeight - 2 * y;
35783                 
35784                 var scale = 1;
35785                 
35786                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35787                     scale = targetWidth / width;
35788                 }
35789                 
35790                 if(x > 0 && y == 0){
35791                     scale = targetHeight / height;
35792                 }
35793                 
35794                 if(x > 0 && y > 0){
35795                     scale = targetWidth / width;
35796                     
35797                     if(width < height){
35798                         scale = targetHeight / height;
35799                     }
35800                 }
35801                 
35802                 context.scale(scale, scale);
35803                 
35804                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35805                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35806
35807                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35808                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35809                 
35810                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35811                 
35812                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35813                 
35814                 break;
35815             default : 
35816                 break;
35817         }
35818         
35819         this.cropData = canvas.toDataURL(this.cropType);
35820         
35821         if(this.fireEvent('crop', this, this.cropData) !== false){
35822             this.process(this.file, this.cropData);
35823         }
35824         
35825         return;
35826         
35827     },
35828     
35829     setThumbBoxSize : function()
35830     {
35831         var width, height;
35832         
35833         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
35834             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
35835             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
35836             
35837             this.minWidth = width;
35838             this.minHeight = height;
35839             
35840             if(this.rotate == 90 || this.rotate == 270){
35841                 this.minWidth = height;
35842                 this.minHeight = width;
35843             }
35844         }
35845         
35846         height = 300;
35847         width = Math.ceil(this.minWidth * height / this.minHeight);
35848         
35849         if(this.minWidth > this.minHeight){
35850             width = 300;
35851             height = Math.ceil(this.minHeight * width / this.minWidth);
35852         }
35853         
35854         this.thumbEl.setStyle({
35855             width : width + 'px',
35856             height : height + 'px'
35857         });
35858
35859         return;
35860             
35861     },
35862     
35863     setThumbBoxPosition : function()
35864     {
35865         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
35866         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
35867         
35868         this.thumbEl.setLeft(x);
35869         this.thumbEl.setTop(y);
35870         
35871     },
35872     
35873     baseRotateLevel : function()
35874     {
35875         this.baseRotate = 1;
35876         
35877         if(
35878                 typeof(this.exif) != 'undefined' &&
35879                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
35880                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
35881         ){
35882             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
35883         }
35884         
35885         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
35886         
35887     },
35888     
35889     baseScaleLevel : function()
35890     {
35891         var width, height;
35892         
35893         if(this.isDocument){
35894             
35895             if(this.baseRotate == 6 || this.baseRotate == 8){
35896             
35897                 height = this.thumbEl.getHeight();
35898                 this.baseScale = height / this.imageEl.OriginWidth;
35899
35900                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
35901                     width = this.thumbEl.getWidth();
35902                     this.baseScale = width / this.imageEl.OriginHeight;
35903                 }
35904
35905                 return;
35906             }
35907
35908             height = this.thumbEl.getHeight();
35909             this.baseScale = height / this.imageEl.OriginHeight;
35910
35911             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
35912                 width = this.thumbEl.getWidth();
35913                 this.baseScale = width / this.imageEl.OriginWidth;
35914             }
35915
35916             return;
35917         }
35918         
35919         if(this.baseRotate == 6 || this.baseRotate == 8){
35920             
35921             width = this.thumbEl.getHeight();
35922             this.baseScale = width / this.imageEl.OriginHeight;
35923             
35924             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
35925                 height = this.thumbEl.getWidth();
35926                 this.baseScale = height / this.imageEl.OriginHeight;
35927             }
35928             
35929             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35930                 height = this.thumbEl.getWidth();
35931                 this.baseScale = height / this.imageEl.OriginHeight;
35932                 
35933                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
35934                     width = this.thumbEl.getHeight();
35935                     this.baseScale = width / this.imageEl.OriginWidth;
35936                 }
35937             }
35938             
35939             return;
35940         }
35941         
35942         width = this.thumbEl.getWidth();
35943         this.baseScale = width / this.imageEl.OriginWidth;
35944         
35945         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
35946             height = this.thumbEl.getHeight();
35947             this.baseScale = height / this.imageEl.OriginHeight;
35948         }
35949         
35950         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35951             
35952             height = this.thumbEl.getHeight();
35953             this.baseScale = height / this.imageEl.OriginHeight;
35954             
35955             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
35956                 width = this.thumbEl.getWidth();
35957                 this.baseScale = width / this.imageEl.OriginWidth;
35958             }
35959             
35960         }
35961         
35962         return;
35963     },
35964     
35965     getScaleLevel : function()
35966     {
35967         return this.baseScale * Math.pow(1.1, this.scale);
35968     },
35969     
35970     onTouchStart : function(e)
35971     {
35972         if(!this.canvasLoaded){
35973             this.beforeSelectFile(e);
35974             return;
35975         }
35976         
35977         var touches = e.browserEvent.touches;
35978         
35979         if(!touches){
35980             return;
35981         }
35982         
35983         if(touches.length == 1){
35984             this.onMouseDown(e);
35985             return;
35986         }
35987         
35988         if(touches.length != 2){
35989             return;
35990         }
35991         
35992         var coords = [];
35993         
35994         for(var i = 0, finger; finger = touches[i]; i++){
35995             coords.push(finger.pageX, finger.pageY);
35996         }
35997         
35998         var x = Math.pow(coords[0] - coords[2], 2);
35999         var y = Math.pow(coords[1] - coords[3], 2);
36000         
36001         this.startDistance = Math.sqrt(x + y);
36002         
36003         this.startScale = this.scale;
36004         
36005         this.pinching = true;
36006         this.dragable = false;
36007         
36008     },
36009     
36010     onTouchMove : function(e)
36011     {
36012         if(!this.pinching && !this.dragable){
36013             return;
36014         }
36015         
36016         var touches = e.browserEvent.touches;
36017         
36018         if(!touches){
36019             return;
36020         }
36021         
36022         if(this.dragable){
36023             this.onMouseMove(e);
36024             return;
36025         }
36026         
36027         var coords = [];
36028         
36029         for(var i = 0, finger; finger = touches[i]; i++){
36030             coords.push(finger.pageX, finger.pageY);
36031         }
36032         
36033         var x = Math.pow(coords[0] - coords[2], 2);
36034         var y = Math.pow(coords[1] - coords[3], 2);
36035         
36036         this.endDistance = Math.sqrt(x + y);
36037         
36038         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36039         
36040         if(!this.zoomable()){
36041             this.scale = this.startScale;
36042             return;
36043         }
36044         
36045         this.draw();
36046         
36047     },
36048     
36049     onTouchEnd : function(e)
36050     {
36051         this.pinching = false;
36052         this.dragable = false;
36053         
36054     },
36055     
36056     process : function(file, crop)
36057     {
36058         if(this.loadMask){
36059             this.maskEl.mask(this.loadingText);
36060         }
36061         
36062         this.xhr = new XMLHttpRequest();
36063         
36064         file.xhr = this.xhr;
36065
36066         this.xhr.open(this.method, this.url, true);
36067         
36068         var headers = {
36069             "Accept": "application/json",
36070             "Cache-Control": "no-cache",
36071             "X-Requested-With": "XMLHttpRequest"
36072         };
36073         
36074         for (var headerName in headers) {
36075             var headerValue = headers[headerName];
36076             if (headerValue) {
36077                 this.xhr.setRequestHeader(headerName, headerValue);
36078             }
36079         }
36080         
36081         var _this = this;
36082         
36083         this.xhr.onload = function()
36084         {
36085             _this.xhrOnLoad(_this.xhr);
36086         }
36087         
36088         this.xhr.onerror = function()
36089         {
36090             _this.xhrOnError(_this.xhr);
36091         }
36092         
36093         var formData = new FormData();
36094
36095         formData.append('returnHTML', 'NO');
36096         
36097         if(crop){
36098             formData.append('crop', crop);
36099         }
36100         
36101         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36102             formData.append(this.paramName, file, file.name);
36103         }
36104         
36105         if(typeof(file.filename) != 'undefined'){
36106             formData.append('filename', file.filename);
36107         }
36108         
36109         if(typeof(file.mimetype) != 'undefined'){
36110             formData.append('mimetype', file.mimetype);
36111         }
36112         
36113         if(this.fireEvent('arrange', this, formData) != false){
36114             this.xhr.send(formData);
36115         };
36116     },
36117     
36118     xhrOnLoad : function(xhr)
36119     {
36120         if(this.loadMask){
36121             this.maskEl.unmask();
36122         }
36123         
36124         if (xhr.readyState !== 4) {
36125             this.fireEvent('exception', this, xhr);
36126             return;
36127         }
36128
36129         var response = Roo.decode(xhr.responseText);
36130         
36131         if(!response.success){
36132             this.fireEvent('exception', this, xhr);
36133             return;
36134         }
36135         
36136         var response = Roo.decode(xhr.responseText);
36137         
36138         this.fireEvent('upload', this, response);
36139         
36140     },
36141     
36142     xhrOnError : function()
36143     {
36144         if(this.loadMask){
36145             this.maskEl.unmask();
36146         }
36147         
36148         Roo.log('xhr on error');
36149         
36150         var response = Roo.decode(xhr.responseText);
36151           
36152         Roo.log(response);
36153         
36154     },
36155     
36156     prepare : function(file)
36157     {   
36158         if(this.loadMask){
36159             this.maskEl.mask(this.loadingText);
36160         }
36161         
36162         this.file = false;
36163         this.exif = {};
36164         
36165         if(typeof(file) === 'string'){
36166             this.loadCanvas(file);
36167             return;
36168         }
36169         
36170         if(!file || !this.urlAPI){
36171             return;
36172         }
36173         
36174         this.file = file;
36175         this.cropType = file.type;
36176         
36177         var _this = this;
36178         
36179         if(this.fireEvent('prepare', this, this.file) != false){
36180             
36181             var reader = new FileReader();
36182             
36183             reader.onload = function (e) {
36184                 if (e.target.error) {
36185                     Roo.log(e.target.error);
36186                     return;
36187                 }
36188                 
36189                 var buffer = e.target.result,
36190                     dataView = new DataView(buffer),
36191                     offset = 2,
36192                     maxOffset = dataView.byteLength - 4,
36193                     markerBytes,
36194                     markerLength;
36195                 
36196                 if (dataView.getUint16(0) === 0xffd8) {
36197                     while (offset < maxOffset) {
36198                         markerBytes = dataView.getUint16(offset);
36199                         
36200                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
36201                             markerLength = dataView.getUint16(offset + 2) + 2;
36202                             if (offset + markerLength > dataView.byteLength) {
36203                                 Roo.log('Invalid meta data: Invalid segment size.');
36204                                 break;
36205                             }
36206                             
36207                             if(markerBytes == 0xffe1){
36208                                 _this.parseExifData(
36209                                     dataView,
36210                                     offset,
36211                                     markerLength
36212                                 );
36213                             }
36214                             
36215                             offset += markerLength;
36216                             
36217                             continue;
36218                         }
36219                         
36220                         break;
36221                     }
36222                     
36223                 }
36224                 
36225                 var url = _this.urlAPI.createObjectURL(_this.file);
36226                 
36227                 _this.loadCanvas(url);
36228                 
36229                 return;
36230             }
36231             
36232             reader.readAsArrayBuffer(this.file);
36233             
36234         }
36235         
36236     },
36237     
36238     parseExifData : function(dataView, offset, length)
36239     {
36240         var tiffOffset = offset + 10,
36241             littleEndian,
36242             dirOffset;
36243     
36244         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36245             // No Exif data, might be XMP data instead
36246             return;
36247         }
36248         
36249         // Check for the ASCII code for "Exif" (0x45786966):
36250         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36251             // No Exif data, might be XMP data instead
36252             return;
36253         }
36254         if (tiffOffset + 8 > dataView.byteLength) {
36255             Roo.log('Invalid Exif data: Invalid segment size.');
36256             return;
36257         }
36258         // Check for the two null bytes:
36259         if (dataView.getUint16(offset + 8) !== 0x0000) {
36260             Roo.log('Invalid Exif data: Missing byte alignment offset.');
36261             return;
36262         }
36263         // Check the byte alignment:
36264         switch (dataView.getUint16(tiffOffset)) {
36265         case 0x4949:
36266             littleEndian = true;
36267             break;
36268         case 0x4D4D:
36269             littleEndian = false;
36270             break;
36271         default:
36272             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
36273             return;
36274         }
36275         // Check for the TIFF tag marker (0x002A):
36276         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
36277             Roo.log('Invalid Exif data: Missing TIFF marker.');
36278             return;
36279         }
36280         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
36281         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
36282         
36283         this.parseExifTags(
36284             dataView,
36285             tiffOffset,
36286             tiffOffset + dirOffset,
36287             littleEndian
36288         );
36289     },
36290     
36291     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
36292     {
36293         var tagsNumber,
36294             dirEndOffset,
36295             i;
36296         if (dirOffset + 6 > dataView.byteLength) {
36297             Roo.log('Invalid Exif data: Invalid directory offset.');
36298             return;
36299         }
36300         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
36301         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
36302         if (dirEndOffset + 4 > dataView.byteLength) {
36303             Roo.log('Invalid Exif data: Invalid directory size.');
36304             return;
36305         }
36306         for (i = 0; i < tagsNumber; i += 1) {
36307             this.parseExifTag(
36308                 dataView,
36309                 tiffOffset,
36310                 dirOffset + 2 + 12 * i, // tag offset
36311                 littleEndian
36312             );
36313         }
36314         // Return the offset to the next directory:
36315         return dataView.getUint32(dirEndOffset, littleEndian);
36316     },
36317     
36318     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
36319     {
36320         var tag = dataView.getUint16(offset, littleEndian);
36321         
36322         this.exif[tag] = this.getExifValue(
36323             dataView,
36324             tiffOffset,
36325             offset,
36326             dataView.getUint16(offset + 2, littleEndian), // tag type
36327             dataView.getUint32(offset + 4, littleEndian), // tag length
36328             littleEndian
36329         );
36330     },
36331     
36332     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
36333     {
36334         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
36335             tagSize,
36336             dataOffset,
36337             values,
36338             i,
36339             str,
36340             c;
36341     
36342         if (!tagType) {
36343             Roo.log('Invalid Exif data: Invalid tag type.');
36344             return;
36345         }
36346         
36347         tagSize = tagType.size * length;
36348         // Determine if the value is contained in the dataOffset bytes,
36349         // or if the value at the dataOffset is a pointer to the actual data:
36350         dataOffset = tagSize > 4 ?
36351                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
36352         if (dataOffset + tagSize > dataView.byteLength) {
36353             Roo.log('Invalid Exif data: Invalid data offset.');
36354             return;
36355         }
36356         if (length === 1) {
36357             return tagType.getValue(dataView, dataOffset, littleEndian);
36358         }
36359         values = [];
36360         for (i = 0; i < length; i += 1) {
36361             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
36362         }
36363         
36364         if (tagType.ascii) {
36365             str = '';
36366             // Concatenate the chars:
36367             for (i = 0; i < values.length; i += 1) {
36368                 c = values[i];
36369                 // Ignore the terminating NULL byte(s):
36370                 if (c === '\u0000') {
36371                     break;
36372                 }
36373                 str += c;
36374             }
36375             return str;
36376         }
36377         return values;
36378     }
36379     
36380 });
36381
36382 Roo.apply(Roo.bootstrap.UploadCropbox, {
36383     tags : {
36384         'Orientation': 0x0112
36385     },
36386     
36387     Orientation: {
36388             1: 0, //'top-left',
36389 //            2: 'top-right',
36390             3: 180, //'bottom-right',
36391 //            4: 'bottom-left',
36392 //            5: 'left-top',
36393             6: 90, //'right-top',
36394 //            7: 'right-bottom',
36395             8: 270 //'left-bottom'
36396     },
36397     
36398     exifTagTypes : {
36399         // byte, 8-bit unsigned int:
36400         1: {
36401             getValue: function (dataView, dataOffset) {
36402                 return dataView.getUint8(dataOffset);
36403             },
36404             size: 1
36405         },
36406         // ascii, 8-bit byte:
36407         2: {
36408             getValue: function (dataView, dataOffset) {
36409                 return String.fromCharCode(dataView.getUint8(dataOffset));
36410             },
36411             size: 1,
36412             ascii: true
36413         },
36414         // short, 16 bit int:
36415         3: {
36416             getValue: function (dataView, dataOffset, littleEndian) {
36417                 return dataView.getUint16(dataOffset, littleEndian);
36418             },
36419             size: 2
36420         },
36421         // long, 32 bit int:
36422         4: {
36423             getValue: function (dataView, dataOffset, littleEndian) {
36424                 return dataView.getUint32(dataOffset, littleEndian);
36425             },
36426             size: 4
36427         },
36428         // rational = two long values, first is numerator, second is denominator:
36429         5: {
36430             getValue: function (dataView, dataOffset, littleEndian) {
36431                 return dataView.getUint32(dataOffset, littleEndian) /
36432                     dataView.getUint32(dataOffset + 4, littleEndian);
36433             },
36434             size: 8
36435         },
36436         // slong, 32 bit signed int:
36437         9: {
36438             getValue: function (dataView, dataOffset, littleEndian) {
36439                 return dataView.getInt32(dataOffset, littleEndian);
36440             },
36441             size: 4
36442         },
36443         // srational, two slongs, first is numerator, second is denominator:
36444         10: {
36445             getValue: function (dataView, dataOffset, littleEndian) {
36446                 return dataView.getInt32(dataOffset, littleEndian) /
36447                     dataView.getInt32(dataOffset + 4, littleEndian);
36448             },
36449             size: 8
36450         }
36451     },
36452     
36453     footer : {
36454         STANDARD : [
36455             {
36456                 tag : 'div',
36457                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36458                 action : 'rotate-left',
36459                 cn : [
36460                     {
36461                         tag : 'button',
36462                         cls : 'btn btn-default',
36463                         html : '<i class="fa fa-undo"></i>'
36464                     }
36465                 ]
36466             },
36467             {
36468                 tag : 'div',
36469                 cls : 'btn-group roo-upload-cropbox-picture',
36470                 action : 'picture',
36471                 cn : [
36472                     {
36473                         tag : 'button',
36474                         cls : 'btn btn-default',
36475                         html : '<i class="fa fa-picture-o"></i>'
36476                     }
36477                 ]
36478             },
36479             {
36480                 tag : 'div',
36481                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36482                 action : 'rotate-right',
36483                 cn : [
36484                     {
36485                         tag : 'button',
36486                         cls : 'btn btn-default',
36487                         html : '<i class="fa fa-repeat"></i>'
36488                     }
36489                 ]
36490             }
36491         ],
36492         DOCUMENT : [
36493             {
36494                 tag : 'div',
36495                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36496                 action : 'rotate-left',
36497                 cn : [
36498                     {
36499                         tag : 'button',
36500                         cls : 'btn btn-default',
36501                         html : '<i class="fa fa-undo"></i>'
36502                     }
36503                 ]
36504             },
36505             {
36506                 tag : 'div',
36507                 cls : 'btn-group roo-upload-cropbox-download',
36508                 action : 'download',
36509                 cn : [
36510                     {
36511                         tag : 'button',
36512                         cls : 'btn btn-default',
36513                         html : '<i class="fa fa-download"></i>'
36514                     }
36515                 ]
36516             },
36517             {
36518                 tag : 'div',
36519                 cls : 'btn-group roo-upload-cropbox-crop',
36520                 action : 'crop',
36521                 cn : [
36522                     {
36523                         tag : 'button',
36524                         cls : 'btn btn-default',
36525                         html : '<i class="fa fa-crop"></i>'
36526                     }
36527                 ]
36528             },
36529             {
36530                 tag : 'div',
36531                 cls : 'btn-group roo-upload-cropbox-trash',
36532                 action : 'trash',
36533                 cn : [
36534                     {
36535                         tag : 'button',
36536                         cls : 'btn btn-default',
36537                         html : '<i class="fa fa-trash"></i>'
36538                     }
36539                 ]
36540             },
36541             {
36542                 tag : 'div',
36543                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36544                 action : 'rotate-right',
36545                 cn : [
36546                     {
36547                         tag : 'button',
36548                         cls : 'btn btn-default',
36549                         html : '<i class="fa fa-repeat"></i>'
36550                     }
36551                 ]
36552             }
36553         ],
36554         ROTATOR : [
36555             {
36556                 tag : 'div',
36557                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36558                 action : 'rotate-left',
36559                 cn : [
36560                     {
36561                         tag : 'button',
36562                         cls : 'btn btn-default',
36563                         html : '<i class="fa fa-undo"></i>'
36564                     }
36565                 ]
36566             },
36567             {
36568                 tag : 'div',
36569                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36570                 action : 'rotate-right',
36571                 cn : [
36572                     {
36573                         tag : 'button',
36574                         cls : 'btn btn-default',
36575                         html : '<i class="fa fa-repeat"></i>'
36576                     }
36577                 ]
36578             }
36579         ]
36580     }
36581 });
36582
36583 /*
36584 * Licence: LGPL
36585 */
36586
36587 /**
36588  * @class Roo.bootstrap.DocumentManager
36589  * @extends Roo.bootstrap.Component
36590  * Bootstrap DocumentManager class
36591  * @cfg {String} paramName default 'imageUpload'
36592  * @cfg {String} toolTipName default 'filename'
36593  * @cfg {String} method default POST
36594  * @cfg {String} url action url
36595  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
36596  * @cfg {Boolean} multiple multiple upload default true
36597  * @cfg {Number} thumbSize default 300
36598  * @cfg {String} fieldLabel
36599  * @cfg {Number} labelWidth default 4
36600  * @cfg {String} labelAlign (left|top) default left
36601  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
36602 * @cfg {Number} labellg set the width of label (1-12)
36603  * @cfg {Number} labelmd set the width of label (1-12)
36604  * @cfg {Number} labelsm set the width of label (1-12)
36605  * @cfg {Number} labelxs set the width of label (1-12)
36606  * 
36607  * @constructor
36608  * Create a new DocumentManager
36609  * @param {Object} config The config object
36610  */
36611
36612 Roo.bootstrap.DocumentManager = function(config){
36613     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
36614     
36615     this.files = [];
36616     this.delegates = [];
36617     
36618     this.addEvents({
36619         /**
36620          * @event initial
36621          * Fire when initial the DocumentManager
36622          * @param {Roo.bootstrap.DocumentManager} this
36623          */
36624         "initial" : true,
36625         /**
36626          * @event inspect
36627          * inspect selected file
36628          * @param {Roo.bootstrap.DocumentManager} this
36629          * @param {File} file
36630          */
36631         "inspect" : true,
36632         /**
36633          * @event exception
36634          * Fire when xhr load exception
36635          * @param {Roo.bootstrap.DocumentManager} this
36636          * @param {XMLHttpRequest} xhr
36637          */
36638         "exception" : true,
36639         /**
36640          * @event afterupload
36641          * Fire when xhr load exception
36642          * @param {Roo.bootstrap.DocumentManager} this
36643          * @param {XMLHttpRequest} xhr
36644          */
36645         "afterupload" : true,
36646         /**
36647          * @event prepare
36648          * prepare the form data
36649          * @param {Roo.bootstrap.DocumentManager} this
36650          * @param {Object} formData
36651          */
36652         "prepare" : true,
36653         /**
36654          * @event remove
36655          * Fire when remove the file
36656          * @param {Roo.bootstrap.DocumentManager} this
36657          * @param {Object} file
36658          */
36659         "remove" : true,
36660         /**
36661          * @event refresh
36662          * Fire after refresh the file
36663          * @param {Roo.bootstrap.DocumentManager} this
36664          */
36665         "refresh" : true,
36666         /**
36667          * @event click
36668          * Fire after click the image
36669          * @param {Roo.bootstrap.DocumentManager} this
36670          * @param {Object} file
36671          */
36672         "click" : true,
36673         /**
36674          * @event edit
36675          * Fire when upload a image and editable set to true
36676          * @param {Roo.bootstrap.DocumentManager} this
36677          * @param {Object} file
36678          */
36679         "edit" : true,
36680         /**
36681          * @event beforeselectfile
36682          * Fire before select file
36683          * @param {Roo.bootstrap.DocumentManager} this
36684          */
36685         "beforeselectfile" : true,
36686         /**
36687          * @event process
36688          * Fire before process file
36689          * @param {Roo.bootstrap.DocumentManager} this
36690          * @param {Object} file
36691          */
36692         "process" : true,
36693         /**
36694          * @event previewrendered
36695          * Fire when preview rendered
36696          * @param {Roo.bootstrap.DocumentManager} this
36697          * @param {Object} file
36698          */
36699         "previewrendered" : true,
36700         /**
36701          */
36702         "previewResize" : true
36703         
36704     });
36705 };
36706
36707 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
36708     
36709     boxes : 0,
36710     inputName : '',
36711     thumbSize : 300,
36712     multiple : true,
36713     files : false,
36714     method : 'POST',
36715     url : '',
36716     paramName : 'imageUpload',
36717     toolTipName : 'filename',
36718     fieldLabel : '',
36719     labelWidth : 4,
36720     labelAlign : 'left',
36721     editable : true,
36722     delegates : false,
36723     xhr : false, 
36724     
36725     labellg : 0,
36726     labelmd : 0,
36727     labelsm : 0,
36728     labelxs : 0,
36729     
36730     getAutoCreate : function()
36731     {   
36732         var managerWidget = {
36733             tag : 'div',
36734             cls : 'roo-document-manager',
36735             cn : [
36736                 {
36737                     tag : 'input',
36738                     cls : 'roo-document-manager-selector',
36739                     type : 'file'
36740                 },
36741                 {
36742                     tag : 'div',
36743                     cls : 'roo-document-manager-uploader',
36744                     cn : [
36745                         {
36746                             tag : 'div',
36747                             cls : 'roo-document-manager-upload-btn',
36748                             html : '<i class="fa fa-plus"></i>'
36749                         }
36750                     ]
36751                     
36752                 }
36753             ]
36754         };
36755         
36756         var content = [
36757             {
36758                 tag : 'div',
36759                 cls : 'column col-md-12',
36760                 cn : managerWidget
36761             }
36762         ];
36763         
36764         if(this.fieldLabel.length){
36765             
36766             content = [
36767                 {
36768                     tag : 'div',
36769                     cls : 'column col-md-12',
36770                     html : this.fieldLabel
36771                 },
36772                 {
36773                     tag : 'div',
36774                     cls : 'column col-md-12',
36775                     cn : managerWidget
36776                 }
36777             ];
36778
36779             if(this.labelAlign == 'left'){
36780                 content = [
36781                     {
36782                         tag : 'div',
36783                         cls : 'column',
36784                         html : this.fieldLabel
36785                     },
36786                     {
36787                         tag : 'div',
36788                         cls : 'column',
36789                         cn : managerWidget
36790                     }
36791                 ];
36792                 
36793                 if(this.labelWidth > 12){
36794                     content[0].style = "width: " + this.labelWidth + 'px';
36795                 }
36796
36797                 if(this.labelWidth < 13 && this.labelmd == 0){
36798                     this.labelmd = this.labelWidth;
36799                 }
36800
36801                 if(this.labellg > 0){
36802                     content[0].cls += ' col-lg-' + this.labellg;
36803                     content[1].cls += ' col-lg-' + (12 - this.labellg);
36804                 }
36805
36806                 if(this.labelmd > 0){
36807                     content[0].cls += ' col-md-' + this.labelmd;
36808                     content[1].cls += ' col-md-' + (12 - this.labelmd);
36809                 }
36810
36811                 if(this.labelsm > 0){
36812                     content[0].cls += ' col-sm-' + this.labelsm;
36813                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
36814                 }
36815
36816                 if(this.labelxs > 0){
36817                     content[0].cls += ' col-xs-' + this.labelxs;
36818                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
36819                 }
36820                 
36821             }
36822         }
36823         
36824         var cfg = {
36825             tag : 'div',
36826             cls : 'row clearfix',
36827             cn : content
36828         };
36829         
36830         return cfg;
36831         
36832     },
36833     
36834     initEvents : function()
36835     {
36836         this.managerEl = this.el.select('.roo-document-manager', true).first();
36837         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36838         
36839         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
36840         this.selectorEl.hide();
36841         
36842         if(this.multiple){
36843             this.selectorEl.attr('multiple', 'multiple');
36844         }
36845         
36846         this.selectorEl.on('change', this.onFileSelected, this);
36847         
36848         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
36849         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36850         
36851         this.uploader.on('click', this.onUploaderClick, this);
36852         
36853         this.renderProgressDialog();
36854         
36855         var _this = this;
36856         
36857         window.addEventListener("resize", function() { _this.refresh(); } );
36858         
36859         this.fireEvent('initial', this);
36860     },
36861     
36862     renderProgressDialog : function()
36863     {
36864         var _this = this;
36865         
36866         this.progressDialog = new Roo.bootstrap.Modal({
36867             cls : 'roo-document-manager-progress-dialog',
36868             allow_close : false,
36869             animate : false,
36870             title : '',
36871             buttons : [
36872                 {
36873                     name  :'cancel',
36874                     weight : 'danger',
36875                     html : 'Cancel'
36876                 }
36877             ], 
36878             listeners : { 
36879                 btnclick : function() {
36880                     _this.uploadCancel();
36881                     this.hide();
36882                 }
36883             }
36884         });
36885          
36886         this.progressDialog.render(Roo.get(document.body));
36887          
36888         this.progress = new Roo.bootstrap.Progress({
36889             cls : 'roo-document-manager-progress',
36890             active : true,
36891             striped : true
36892         });
36893         
36894         this.progress.render(this.progressDialog.getChildContainer());
36895         
36896         this.progressBar = new Roo.bootstrap.ProgressBar({
36897             cls : 'roo-document-manager-progress-bar',
36898             aria_valuenow : 0,
36899             aria_valuemin : 0,
36900             aria_valuemax : 12,
36901             panel : 'success'
36902         });
36903         
36904         this.progressBar.render(this.progress.getChildContainer());
36905     },
36906     
36907     onUploaderClick : function(e)
36908     {
36909         e.preventDefault();
36910      
36911         if(this.fireEvent('beforeselectfile', this) != false){
36912             this.selectorEl.dom.click();
36913         }
36914         
36915     },
36916     
36917     onFileSelected : function(e)
36918     {
36919         e.preventDefault();
36920         
36921         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36922             return;
36923         }
36924         
36925         Roo.each(this.selectorEl.dom.files, function(file){
36926             if(this.fireEvent('inspect', this, file) != false){
36927                 this.files.push(file);
36928             }
36929         }, this);
36930         
36931         this.queue();
36932         
36933     },
36934     
36935     queue : function()
36936     {
36937         this.selectorEl.dom.value = '';
36938         
36939         if(!this.files || !this.files.length){
36940             return;
36941         }
36942         
36943         if(this.boxes > 0 && this.files.length > this.boxes){
36944             this.files = this.files.slice(0, this.boxes);
36945         }
36946         
36947         this.uploader.show();
36948         
36949         if(this.boxes > 0 && this.files.length > this.boxes - 1){
36950             this.uploader.hide();
36951         }
36952         
36953         var _this = this;
36954         
36955         var files = [];
36956         
36957         var docs = [];
36958         
36959         Roo.each(this.files, function(file){
36960             
36961             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
36962                 var f = this.renderPreview(file);
36963                 files.push(f);
36964                 return;
36965             }
36966             
36967             if(file.type.indexOf('image') != -1){
36968                 this.delegates.push(
36969                     (function(){
36970                         _this.process(file);
36971                     }).createDelegate(this)
36972                 );
36973         
36974                 return;
36975             }
36976             
36977             docs.push(
36978                 (function(){
36979                     _this.process(file);
36980                 }).createDelegate(this)
36981             );
36982             
36983         }, this);
36984         
36985         this.files = files;
36986         
36987         this.delegates = this.delegates.concat(docs);
36988         
36989         if(!this.delegates.length){
36990             this.refresh();
36991             return;
36992         }
36993         
36994         this.progressBar.aria_valuemax = this.delegates.length;
36995         
36996         this.arrange();
36997         
36998         return;
36999     },
37000     
37001     arrange : function()
37002     {
37003         if(!this.delegates.length){
37004             this.progressDialog.hide();
37005             this.refresh();
37006             return;
37007         }
37008         
37009         var delegate = this.delegates.shift();
37010         
37011         this.progressDialog.show();
37012         
37013         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37014         
37015         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37016         
37017         delegate();
37018     },
37019     
37020     refresh : function()
37021     {
37022         this.uploader.show();
37023         
37024         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37025             this.uploader.hide();
37026         }
37027         
37028         Roo.isTouch ? this.closable(false) : this.closable(true);
37029         
37030         this.fireEvent('refresh', this);
37031     },
37032     
37033     onRemove : function(e, el, o)
37034     {
37035         e.preventDefault();
37036         
37037         this.fireEvent('remove', this, o);
37038         
37039     },
37040     
37041     remove : function(o)
37042     {
37043         var files = [];
37044         
37045         Roo.each(this.files, function(file){
37046             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37047                 files.push(file);
37048                 return;
37049             }
37050
37051             o.target.remove();
37052
37053         }, this);
37054         
37055         this.files = files;
37056         
37057         this.refresh();
37058     },
37059     
37060     clear : function()
37061     {
37062         Roo.each(this.files, function(file){
37063             if(!file.target){
37064                 return;
37065             }
37066             
37067             file.target.remove();
37068
37069         }, this);
37070         
37071         this.files = [];
37072         
37073         this.refresh();
37074     },
37075     
37076     onClick : function(e, el, o)
37077     {
37078         e.preventDefault();
37079         
37080         this.fireEvent('click', this, o);
37081         
37082     },
37083     
37084     closable : function(closable)
37085     {
37086         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37087             
37088             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37089             
37090             if(closable){
37091                 el.show();
37092                 return;
37093             }
37094             
37095             el.hide();
37096             
37097         }, this);
37098     },
37099     
37100     xhrOnLoad : function(xhr)
37101     {
37102         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37103             el.remove();
37104         }, this);
37105         
37106         if (xhr.readyState !== 4) {
37107             this.arrange();
37108             this.fireEvent('exception', this, xhr);
37109             return;
37110         }
37111
37112         var response = Roo.decode(xhr.responseText);
37113         
37114         if(!response.success){
37115             this.arrange();
37116             this.fireEvent('exception', this, xhr);
37117             return;
37118         }
37119         
37120         var file = this.renderPreview(response.data);
37121         
37122         this.files.push(file);
37123         
37124         this.arrange();
37125         
37126         this.fireEvent('afterupload', this, xhr);
37127         
37128     },
37129     
37130     xhrOnError : function(xhr)
37131     {
37132         Roo.log('xhr on error');
37133         
37134         var response = Roo.decode(xhr.responseText);
37135           
37136         Roo.log(response);
37137         
37138         this.arrange();
37139     },
37140     
37141     process : function(file)
37142     {
37143         if(this.fireEvent('process', this, file) !== false){
37144             if(this.editable && file.type.indexOf('image') != -1){
37145                 this.fireEvent('edit', this, file);
37146                 return;
37147             }
37148
37149             this.uploadStart(file, false);
37150
37151             return;
37152         }
37153         
37154     },
37155     
37156     uploadStart : function(file, crop)
37157     {
37158         this.xhr = new XMLHttpRequest();
37159         
37160         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37161             this.arrange();
37162             return;
37163         }
37164         
37165         file.xhr = this.xhr;
37166             
37167         this.managerEl.createChild({
37168             tag : 'div',
37169             cls : 'roo-document-manager-loading',
37170             cn : [
37171                 {
37172                     tag : 'div',
37173                     tooltip : file.name,
37174                     cls : 'roo-document-manager-thumb',
37175                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37176                 }
37177             ]
37178
37179         });
37180
37181         this.xhr.open(this.method, this.url, true);
37182         
37183         var headers = {
37184             "Accept": "application/json",
37185             "Cache-Control": "no-cache",
37186             "X-Requested-With": "XMLHttpRequest"
37187         };
37188         
37189         for (var headerName in headers) {
37190             var headerValue = headers[headerName];
37191             if (headerValue) {
37192                 this.xhr.setRequestHeader(headerName, headerValue);
37193             }
37194         }
37195         
37196         var _this = this;
37197         
37198         this.xhr.onload = function()
37199         {
37200             _this.xhrOnLoad(_this.xhr);
37201         }
37202         
37203         this.xhr.onerror = function()
37204         {
37205             _this.xhrOnError(_this.xhr);
37206         }
37207         
37208         var formData = new FormData();
37209
37210         formData.append('returnHTML', 'NO');
37211         
37212         if(crop){
37213             formData.append('crop', crop);
37214         }
37215         
37216         formData.append(this.paramName, file, file.name);
37217         
37218         var options = {
37219             file : file, 
37220             manually : false
37221         };
37222         
37223         if(this.fireEvent('prepare', this, formData, options) != false){
37224             
37225             if(options.manually){
37226                 return;
37227             }
37228             
37229             this.xhr.send(formData);
37230             return;
37231         };
37232         
37233         this.uploadCancel();
37234     },
37235     
37236     uploadCancel : function()
37237     {
37238         if (this.xhr) {
37239             this.xhr.abort();
37240         }
37241         
37242         this.delegates = [];
37243         
37244         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37245             el.remove();
37246         }, this);
37247         
37248         this.arrange();
37249     },
37250     
37251     renderPreview : function(file)
37252     {
37253         if(typeof(file.target) != 'undefined' && file.target){
37254             return file;
37255         }
37256         
37257         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
37258         
37259         var previewEl = this.managerEl.createChild({
37260             tag : 'div',
37261             cls : 'roo-document-manager-preview',
37262             cn : [
37263                 {
37264                     tag : 'div',
37265                     tooltip : file[this.toolTipName],
37266                     cls : 'roo-document-manager-thumb',
37267                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
37268                 },
37269                 {
37270                     tag : 'button',
37271                     cls : 'close',
37272                     html : '<i class="fa fa-times-circle"></i>'
37273                 }
37274             ]
37275         });
37276
37277         var close = previewEl.select('button.close', true).first();
37278
37279         close.on('click', this.onRemove, this, file);
37280
37281         file.target = previewEl;
37282
37283         var image = previewEl.select('img', true).first();
37284         
37285         var _this = this;
37286         
37287         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
37288         
37289         image.on('click', this.onClick, this, file);
37290         
37291         this.fireEvent('previewrendered', this, file);
37292         
37293         return file;
37294         
37295     },
37296     
37297     onPreviewLoad : function(file, image)
37298     {
37299         if(typeof(file.target) == 'undefined' || !file.target){
37300             return;
37301         }
37302         
37303         var width = image.dom.naturalWidth || image.dom.width;
37304         var height = image.dom.naturalHeight || image.dom.height;
37305         
37306         if(!this.previewResize) {
37307             return;
37308         }
37309         
37310         if(width > height){
37311             file.target.addClass('wide');
37312             return;
37313         }
37314         
37315         file.target.addClass('tall');
37316         return;
37317         
37318     },
37319     
37320     uploadFromSource : function(file, crop)
37321     {
37322         this.xhr = new XMLHttpRequest();
37323         
37324         this.managerEl.createChild({
37325             tag : 'div',
37326             cls : 'roo-document-manager-loading',
37327             cn : [
37328                 {
37329                     tag : 'div',
37330                     tooltip : file.name,
37331                     cls : 'roo-document-manager-thumb',
37332                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37333                 }
37334             ]
37335
37336         });
37337
37338         this.xhr.open(this.method, this.url, true);
37339         
37340         var headers = {
37341             "Accept": "application/json",
37342             "Cache-Control": "no-cache",
37343             "X-Requested-With": "XMLHttpRequest"
37344         };
37345         
37346         for (var headerName in headers) {
37347             var headerValue = headers[headerName];
37348             if (headerValue) {
37349                 this.xhr.setRequestHeader(headerName, headerValue);
37350             }
37351         }
37352         
37353         var _this = this;
37354         
37355         this.xhr.onload = function()
37356         {
37357             _this.xhrOnLoad(_this.xhr);
37358         }
37359         
37360         this.xhr.onerror = function()
37361         {
37362             _this.xhrOnError(_this.xhr);
37363         }
37364         
37365         var formData = new FormData();
37366
37367         formData.append('returnHTML', 'NO');
37368         
37369         formData.append('crop', crop);
37370         
37371         if(typeof(file.filename) != 'undefined'){
37372             formData.append('filename', file.filename);
37373         }
37374         
37375         if(typeof(file.mimetype) != 'undefined'){
37376             formData.append('mimetype', file.mimetype);
37377         }
37378         
37379         Roo.log(formData);
37380         
37381         if(this.fireEvent('prepare', this, formData) != false){
37382             this.xhr.send(formData);
37383         };
37384     }
37385 });
37386
37387 /*
37388 * Licence: LGPL
37389 */
37390
37391 /**
37392  * @class Roo.bootstrap.DocumentViewer
37393  * @extends Roo.bootstrap.Component
37394  * Bootstrap DocumentViewer class
37395  * @cfg {Boolean} showDownload (true|false) show download button (default true)
37396  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
37397  * 
37398  * @constructor
37399  * Create a new DocumentViewer
37400  * @param {Object} config The config object
37401  */
37402
37403 Roo.bootstrap.DocumentViewer = function(config){
37404     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
37405     
37406     this.addEvents({
37407         /**
37408          * @event initial
37409          * Fire after initEvent
37410          * @param {Roo.bootstrap.DocumentViewer} this
37411          */
37412         "initial" : true,
37413         /**
37414          * @event click
37415          * Fire after click
37416          * @param {Roo.bootstrap.DocumentViewer} this
37417          */
37418         "click" : true,
37419         /**
37420          * @event download
37421          * Fire after download button
37422          * @param {Roo.bootstrap.DocumentViewer} this
37423          */
37424         "download" : true,
37425         /**
37426          * @event trash
37427          * Fire after trash button
37428          * @param {Roo.bootstrap.DocumentViewer} this
37429          */
37430         "trash" : true
37431         
37432     });
37433 };
37434
37435 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
37436     
37437     showDownload : true,
37438     
37439     showTrash : true,
37440     
37441     getAutoCreate : function()
37442     {
37443         var cfg = {
37444             tag : 'div',
37445             cls : 'roo-document-viewer',
37446             cn : [
37447                 {
37448                     tag : 'div',
37449                     cls : 'roo-document-viewer-body',
37450                     cn : [
37451                         {
37452                             tag : 'div',
37453                             cls : 'roo-document-viewer-thumb',
37454                             cn : [
37455                                 {
37456                                     tag : 'img',
37457                                     cls : 'roo-document-viewer-image'
37458                                 }
37459                             ]
37460                         }
37461                     ]
37462                 },
37463                 {
37464                     tag : 'div',
37465                     cls : 'roo-document-viewer-footer',
37466                     cn : {
37467                         tag : 'div',
37468                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
37469                         cn : [
37470                             {
37471                                 tag : 'div',
37472                                 cls : 'btn-group roo-document-viewer-download',
37473                                 cn : [
37474                                     {
37475                                         tag : 'button',
37476                                         cls : 'btn btn-default',
37477                                         html : '<i class="fa fa-download"></i>'
37478                                     }
37479                                 ]
37480                             },
37481                             {
37482                                 tag : 'div',
37483                                 cls : 'btn-group roo-document-viewer-trash',
37484                                 cn : [
37485                                     {
37486                                         tag : 'button',
37487                                         cls : 'btn btn-default',
37488                                         html : '<i class="fa fa-trash"></i>'
37489                                     }
37490                                 ]
37491                             }
37492                         ]
37493                     }
37494                 }
37495             ]
37496         };
37497         
37498         return cfg;
37499     },
37500     
37501     initEvents : function()
37502     {
37503         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
37504         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37505         
37506         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
37507         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37508         
37509         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
37510         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37511         
37512         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
37513         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
37514         
37515         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
37516         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
37517         
37518         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
37519         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
37520         
37521         this.bodyEl.on('click', this.onClick, this);
37522         this.downloadBtn.on('click', this.onDownload, this);
37523         this.trashBtn.on('click', this.onTrash, this);
37524         
37525         this.downloadBtn.hide();
37526         this.trashBtn.hide();
37527         
37528         if(this.showDownload){
37529             this.downloadBtn.show();
37530         }
37531         
37532         if(this.showTrash){
37533             this.trashBtn.show();
37534         }
37535         
37536         if(!this.showDownload && !this.showTrash) {
37537             this.footerEl.hide();
37538         }
37539         
37540     },
37541     
37542     initial : function()
37543     {
37544         this.fireEvent('initial', this);
37545         
37546     },
37547     
37548     onClick : function(e)
37549     {
37550         e.preventDefault();
37551         
37552         this.fireEvent('click', this);
37553     },
37554     
37555     onDownload : function(e)
37556     {
37557         e.preventDefault();
37558         
37559         this.fireEvent('download', this);
37560     },
37561     
37562     onTrash : function(e)
37563     {
37564         e.preventDefault();
37565         
37566         this.fireEvent('trash', this);
37567     }
37568     
37569 });
37570 /*
37571  * - LGPL
37572  *
37573  * FieldLabel
37574  * 
37575  */
37576
37577 /**
37578  * @class Roo.bootstrap.form.FieldLabel
37579  * @extends Roo.bootstrap.Component
37580  * Bootstrap FieldLabel class
37581  * @cfg {String} html contents of the element
37582  * @cfg {String} tag tag of the element default label
37583  * @cfg {String} cls class of the element
37584  * @cfg {String} target label target 
37585  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
37586  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
37587  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
37588  * @cfg {String} iconTooltip default "This field is required"
37589  * @cfg {String} indicatorpos (left|right) default left
37590  * 
37591  * @constructor
37592  * Create a new FieldLabel
37593  * @param {Object} config The config object
37594  */
37595
37596 Roo.bootstrap.form.FieldLabel = function(config){
37597     Roo.bootstrap.Element.superclass.constructor.call(this, config);
37598     
37599     this.addEvents({
37600             /**
37601              * @event invalid
37602              * Fires after the field has been marked as invalid.
37603              * @param {Roo.form.FieldLabel} this
37604              * @param {String} msg The validation message
37605              */
37606             invalid : true,
37607             /**
37608              * @event valid
37609              * Fires after the field has been validated with no errors.
37610              * @param {Roo.form.FieldLabel} this
37611              */
37612             valid : true
37613         });
37614 };
37615
37616 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
37617     
37618     tag: 'label',
37619     cls: '',
37620     html: '',
37621     target: '',
37622     allowBlank : true,
37623     invalidClass : 'has-warning',
37624     validClass : 'has-success',
37625     iconTooltip : 'This field is required',
37626     indicatorpos : 'left',
37627     
37628     getAutoCreate : function(){
37629         
37630         var cls = "";
37631         if (!this.allowBlank) {
37632             cls  = "visible";
37633         }
37634         
37635         var cfg = {
37636             tag : this.tag,
37637             cls : 'roo-bootstrap-field-label ' + this.cls,
37638             for : this.target,
37639             cn : [
37640                 {
37641                     tag : 'i',
37642                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
37643                     tooltip : this.iconTooltip
37644                 },
37645                 {
37646                     tag : 'span',
37647                     html : this.html
37648                 }
37649             ] 
37650         };
37651         
37652         if(this.indicatorpos == 'right'){
37653             var cfg = {
37654                 tag : this.tag,
37655                 cls : 'roo-bootstrap-field-label ' + this.cls,
37656                 for : this.target,
37657                 cn : [
37658                     {
37659                         tag : 'span',
37660                         html : this.html
37661                     },
37662                     {
37663                         tag : 'i',
37664                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
37665                         tooltip : this.iconTooltip
37666                     }
37667                 ] 
37668             };
37669         }
37670         
37671         return cfg;
37672     },
37673     
37674     initEvents: function() 
37675     {
37676         Roo.bootstrap.Element.superclass.initEvents.call(this);
37677         
37678         this.indicator = this.indicatorEl();
37679         
37680         if(this.indicator){
37681             this.indicator.removeClass('visible');
37682             this.indicator.addClass('invisible');
37683         }
37684         
37685         Roo.bootstrap.form.FieldLabel.register(this);
37686     },
37687     
37688     indicatorEl : function()
37689     {
37690         var indicator = this.el.select('i.roo-required-indicator',true).first();
37691         
37692         if(!indicator){
37693             return false;
37694         }
37695         
37696         return indicator;
37697         
37698     },
37699     
37700     /**
37701      * Mark this field as valid
37702      */
37703     markValid : function()
37704     {
37705         if(this.indicator){
37706             this.indicator.removeClass('visible');
37707             this.indicator.addClass('invisible');
37708         }
37709         if (Roo.bootstrap.version == 3) {
37710             this.el.removeClass(this.invalidClass);
37711             this.el.addClass(this.validClass);
37712         } else {
37713             this.el.removeClass('is-invalid');
37714             this.el.addClass('is-valid');
37715         }
37716         
37717         
37718         this.fireEvent('valid', this);
37719     },
37720     
37721     /**
37722      * Mark this field as invalid
37723      * @param {String} msg The validation message
37724      */
37725     markInvalid : function(msg)
37726     {
37727         if(this.indicator){
37728             this.indicator.removeClass('invisible');
37729             this.indicator.addClass('visible');
37730         }
37731           if (Roo.bootstrap.version == 3) {
37732             this.el.removeClass(this.validClass);
37733             this.el.addClass(this.invalidClass);
37734         } else {
37735             this.el.removeClass('is-valid');
37736             this.el.addClass('is-invalid');
37737         }
37738         
37739         
37740         this.fireEvent('invalid', this, msg);
37741     }
37742     
37743    
37744 });
37745
37746 Roo.apply(Roo.bootstrap.form.FieldLabel, {
37747     
37748     groups: {},
37749     
37750      /**
37751     * register a FieldLabel Group
37752     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
37753     */
37754     register : function(label)
37755     {
37756         if(this.groups.hasOwnProperty(label.target)){
37757             return;
37758         }
37759      
37760         this.groups[label.target] = label;
37761         
37762     },
37763     /**
37764     * fetch a FieldLabel Group based on the target
37765     * @param {string} target
37766     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
37767     */
37768     get: function(target) {
37769         if (typeof(this.groups[target]) == 'undefined') {
37770             return false;
37771         }
37772         
37773         return this.groups[target] ;
37774     }
37775 });
37776
37777  
37778
37779  /*
37780  * - LGPL
37781  *
37782  * page DateSplitField.
37783  * 
37784  */
37785
37786
37787 /**
37788  * @class Roo.bootstrap.form.DateSplitField
37789  * @extends Roo.bootstrap.Component
37790  * Bootstrap DateSplitField class
37791  * @cfg {string} fieldLabel - the label associated
37792  * @cfg {Number} labelWidth set the width of label (0-12)
37793  * @cfg {String} labelAlign (top|left)
37794  * @cfg {Boolean} dayAllowBlank (true|false) default false
37795  * @cfg {Boolean} monthAllowBlank (true|false) default false
37796  * @cfg {Boolean} yearAllowBlank (true|false) default false
37797  * @cfg {string} dayPlaceholder 
37798  * @cfg {string} monthPlaceholder
37799  * @cfg {string} yearPlaceholder
37800  * @cfg {string} dayFormat default 'd'
37801  * @cfg {string} monthFormat default 'm'
37802  * @cfg {string} yearFormat default 'Y'
37803  * @cfg {Number} labellg set the width of label (1-12)
37804  * @cfg {Number} labelmd set the width of label (1-12)
37805  * @cfg {Number} labelsm set the width of label (1-12)
37806  * @cfg {Number} labelxs set the width of label (1-12)
37807
37808  *     
37809  * @constructor
37810  * Create a new DateSplitField
37811  * @param {Object} config The config object
37812  */
37813
37814 Roo.bootstrap.form.DateSplitField = function(config){
37815     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
37816     
37817     this.addEvents({
37818         // raw events
37819          /**
37820          * @event years
37821          * getting the data of years
37822          * @param {Roo.bootstrap.form.DateSplitField} this
37823          * @param {Object} years
37824          */
37825         "years" : true,
37826         /**
37827          * @event days
37828          * getting the data of days
37829          * @param {Roo.bootstrap.form.DateSplitField} this
37830          * @param {Object} days
37831          */
37832         "days" : true,
37833         /**
37834          * @event invalid
37835          * Fires after the field has been marked as invalid.
37836          * @param {Roo.form.Field} this
37837          * @param {String} msg The validation message
37838          */
37839         invalid : true,
37840        /**
37841          * @event valid
37842          * Fires after the field has been validated with no errors.
37843          * @param {Roo.form.Field} this
37844          */
37845         valid : true
37846     });
37847 };
37848
37849 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
37850     
37851     fieldLabel : '',
37852     labelAlign : 'top',
37853     labelWidth : 3,
37854     dayAllowBlank : false,
37855     monthAllowBlank : false,
37856     yearAllowBlank : false,
37857     dayPlaceholder : '',
37858     monthPlaceholder : '',
37859     yearPlaceholder : '',
37860     dayFormat : 'd',
37861     monthFormat : 'm',
37862     yearFormat : 'Y',
37863     isFormField : true,
37864     labellg : 0,
37865     labelmd : 0,
37866     labelsm : 0,
37867     labelxs : 0,
37868     
37869     getAutoCreate : function()
37870     {
37871         var cfg = {
37872             tag : 'div',
37873             cls : 'row roo-date-split-field-group',
37874             cn : [
37875                 {
37876                     tag : 'input',
37877                     type : 'hidden',
37878                     cls : 'form-hidden-field roo-date-split-field-group-value',
37879                     name : this.name
37880                 }
37881             ]
37882         };
37883         
37884         var labelCls = 'col-md-12';
37885         var contentCls = 'col-md-4';
37886         
37887         if(this.fieldLabel){
37888             
37889             var label = {
37890                 tag : 'div',
37891                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
37892                 cn : [
37893                     {
37894                         tag : 'label',
37895                         html : this.fieldLabel
37896                     }
37897                 ]
37898             };
37899             
37900             if(this.labelAlign == 'left'){
37901             
37902                 if(this.labelWidth > 12){
37903                     label.style = "width: " + this.labelWidth + 'px';
37904                 }
37905
37906                 if(this.labelWidth < 13 && this.labelmd == 0){
37907                     this.labelmd = this.labelWidth;
37908                 }
37909
37910                 if(this.labellg > 0){
37911                     labelCls = ' col-lg-' + this.labellg;
37912                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
37913                 }
37914
37915                 if(this.labelmd > 0){
37916                     labelCls = ' col-md-' + this.labelmd;
37917                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
37918                 }
37919
37920                 if(this.labelsm > 0){
37921                     labelCls = ' col-sm-' + this.labelsm;
37922                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
37923                 }
37924
37925                 if(this.labelxs > 0){
37926                     labelCls = ' col-xs-' + this.labelxs;
37927                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
37928                 }
37929             }
37930             
37931             label.cls += ' ' + labelCls;
37932             
37933             cfg.cn.push(label);
37934         }
37935         
37936         Roo.each(['day', 'month', 'year'], function(t){
37937             cfg.cn.push({
37938                 tag : 'div',
37939                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
37940             });
37941         }, this);
37942         
37943         return cfg;
37944     },
37945     
37946     inputEl: function ()
37947     {
37948         return this.el.select('.roo-date-split-field-group-value', true).first();
37949     },
37950     
37951     onRender : function(ct, position) 
37952     {
37953         var _this = this;
37954         
37955         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
37956         
37957         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
37958         
37959         this.dayField = new Roo.bootstrap.form.ComboBox({
37960             allowBlank : this.dayAllowBlank,
37961             alwaysQuery : true,
37962             displayField : 'value',
37963             editable : false,
37964             fieldLabel : '',
37965             forceSelection : true,
37966             mode : 'local',
37967             placeholder : this.dayPlaceholder,
37968             selectOnFocus : true,
37969             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
37970             triggerAction : 'all',
37971             typeAhead : true,
37972             valueField : 'value',
37973             store : new Roo.data.SimpleStore({
37974                 data : (function() {    
37975                     var days = [];
37976                     _this.fireEvent('days', _this, days);
37977                     return days;
37978                 })(),
37979                 fields : [ 'value' ]
37980             }),
37981             listeners : {
37982                 select : function (_self, record, index)
37983                 {
37984                     _this.setValue(_this.getValue());
37985                 }
37986             }
37987         });
37988
37989         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
37990         
37991         this.monthField = new Roo.bootstrap.form.MonthField({
37992             after : '<i class=\"fa fa-calendar\"></i>',
37993             allowBlank : this.monthAllowBlank,
37994             placeholder : this.monthPlaceholder,
37995             readOnly : true,
37996             listeners : {
37997                 render : function (_self)
37998                 {
37999                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
38000                         e.preventDefault();
38001                         _self.focus();
38002                     });
38003                 },
38004                 select : function (_self, oldvalue, newvalue)
38005                 {
38006                     _this.setValue(_this.getValue());
38007                 }
38008             }
38009         });
38010         
38011         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38012         
38013         this.yearField = new Roo.bootstrap.form.ComboBox({
38014             allowBlank : this.yearAllowBlank,
38015             alwaysQuery : true,
38016             displayField : 'value',
38017             editable : false,
38018             fieldLabel : '',
38019             forceSelection : true,
38020             mode : 'local',
38021             placeholder : this.yearPlaceholder,
38022             selectOnFocus : true,
38023             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38024             triggerAction : 'all',
38025             typeAhead : true,
38026             valueField : 'value',
38027             store : new Roo.data.SimpleStore({
38028                 data : (function() {
38029                     var years = [];
38030                     _this.fireEvent('years', _this, years);
38031                     return years;
38032                 })(),
38033                 fields : [ 'value' ]
38034             }),
38035             listeners : {
38036                 select : function (_self, record, index)
38037                 {
38038                     _this.setValue(_this.getValue());
38039                 }
38040             }
38041         });
38042
38043         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38044     },
38045     
38046     setValue : function(v, format)
38047     {
38048         this.inputEl.dom.value = v;
38049         
38050         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38051         
38052         var d = Date.parseDate(v, f);
38053         
38054         if(!d){
38055             this.validate();
38056             return;
38057         }
38058         
38059         this.setDay(d.format(this.dayFormat));
38060         this.setMonth(d.format(this.monthFormat));
38061         this.setYear(d.format(this.yearFormat));
38062         
38063         this.validate();
38064         
38065         return;
38066     },
38067     
38068     setDay : function(v)
38069     {
38070         this.dayField.setValue(v);
38071         this.inputEl.dom.value = this.getValue();
38072         this.validate();
38073         return;
38074     },
38075     
38076     setMonth : function(v)
38077     {
38078         this.monthField.setValue(v, true);
38079         this.inputEl.dom.value = this.getValue();
38080         this.validate();
38081         return;
38082     },
38083     
38084     setYear : function(v)
38085     {
38086         this.yearField.setValue(v);
38087         this.inputEl.dom.value = this.getValue();
38088         this.validate();
38089         return;
38090     },
38091     
38092     getDay : function()
38093     {
38094         return this.dayField.getValue();
38095     },
38096     
38097     getMonth : function()
38098     {
38099         return this.monthField.getValue();
38100     },
38101     
38102     getYear : function()
38103     {
38104         return this.yearField.getValue();
38105     },
38106     
38107     getValue : function()
38108     {
38109         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38110         
38111         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38112         
38113         return date;
38114     },
38115     
38116     reset : function()
38117     {
38118         this.setDay('');
38119         this.setMonth('');
38120         this.setYear('');
38121         this.inputEl.dom.value = '';
38122         this.validate();
38123         return;
38124     },
38125     
38126     validate : function()
38127     {
38128         var d = this.dayField.validate();
38129         var m = this.monthField.validate();
38130         var y = this.yearField.validate();
38131         
38132         var valid = true;
38133         
38134         if(
38135                 (!this.dayAllowBlank && !d) ||
38136                 (!this.monthAllowBlank && !m) ||
38137                 (!this.yearAllowBlank && !y)
38138         ){
38139             valid = false;
38140         }
38141         
38142         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38143             return valid;
38144         }
38145         
38146         if(valid){
38147             this.markValid();
38148             return valid;
38149         }
38150         
38151         this.markInvalid();
38152         
38153         return valid;
38154     },
38155     
38156     markValid : function()
38157     {
38158         
38159         var label = this.el.select('label', true).first();
38160         var icon = this.el.select('i.fa-star', true).first();
38161
38162         if(label && icon){
38163             icon.remove();
38164         }
38165         
38166         this.fireEvent('valid', this);
38167     },
38168     
38169      /**
38170      * Mark this field as invalid
38171      * @param {String} msg The validation message
38172      */
38173     markInvalid : function(msg)
38174     {
38175         
38176         var label = this.el.select('label', true).first();
38177         var icon = this.el.select('i.fa-star', true).first();
38178
38179         if(label && !icon){
38180             this.el.select('.roo-date-split-field-label', true).createChild({
38181                 tag : 'i',
38182                 cls : 'text-danger fa fa-lg fa-star',
38183                 tooltip : 'This field is required',
38184                 style : 'margin-right:5px;'
38185             }, label, true);
38186         }
38187         
38188         this.fireEvent('invalid', this, msg);
38189     },
38190     
38191     clearInvalid : function()
38192     {
38193         var label = this.el.select('label', true).first();
38194         var icon = this.el.select('i.fa-star', true).first();
38195
38196         if(label && icon){
38197             icon.remove();
38198         }
38199         
38200         this.fireEvent('valid', this);
38201     },
38202     
38203     getName: function()
38204     {
38205         return this.name;
38206     }
38207     
38208 });
38209
38210  
38211
38212 /**
38213  * @class Roo.bootstrap.LayoutMasonry
38214  * @extends Roo.bootstrap.Component
38215  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
38216  * Bootstrap Layout Masonry class
38217  *
38218  * This is based on 
38219  * http://masonry.desandro.com
38220  *
38221  * The idea is to render all the bricks based on vertical width...
38222  *
38223  * The original code extends 'outlayer' - we might need to use that....
38224
38225  * @constructor
38226  * Create a new Element
38227  * @param {Object} config The config object
38228  */
38229
38230 Roo.bootstrap.LayoutMasonry = function(config){
38231     
38232     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
38233     
38234     this.bricks = [];
38235     
38236     Roo.bootstrap.LayoutMasonry.register(this);
38237     
38238     this.addEvents({
38239         // raw events
38240         /**
38241          * @event layout
38242          * Fire after layout the items
38243          * @param {Roo.bootstrap.LayoutMasonry} this
38244          * @param {Roo.EventObject} e
38245          */
38246         "layout" : true
38247     });
38248     
38249 };
38250
38251 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
38252     
38253     /**
38254      * @cfg {Boolean} isLayoutInstant = no animation?
38255      */   
38256     isLayoutInstant : false, // needed?
38257    
38258     /**
38259      * @cfg {Number} boxWidth  width of the columns
38260      */   
38261     boxWidth : 450,
38262     
38263       /**
38264      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
38265      */   
38266     boxHeight : 0,
38267     
38268     /**
38269      * @cfg {Number} padWidth padding below box..
38270      */   
38271     padWidth : 10, 
38272     
38273     /**
38274      * @cfg {Number} gutter gutter width..
38275      */   
38276     gutter : 10,
38277     
38278      /**
38279      * @cfg {Number} maxCols maximum number of columns
38280      */   
38281     
38282     maxCols: 0,
38283     
38284     /**
38285      * @cfg {Boolean} isAutoInitial defalut true
38286      */   
38287     isAutoInitial : true, 
38288     
38289     containerWidth: 0,
38290     
38291     /**
38292      * @cfg {Boolean} isHorizontal defalut false
38293      */   
38294     isHorizontal : false, 
38295
38296     currentSize : null,
38297     
38298     tag: 'div',
38299     
38300     cls: '',
38301     
38302     bricks: null, //CompositeElement
38303     
38304     cols : 1,
38305     
38306     _isLayoutInited : false,
38307     
38308 //    isAlternative : false, // only use for vertical layout...
38309     
38310     /**
38311      * @cfg {Number} alternativePadWidth padding below box..
38312      */   
38313     alternativePadWidth : 50,
38314     
38315     selectedBrick : [],
38316     
38317     getAutoCreate : function(){
38318         
38319         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
38320         
38321         var cfg = {
38322             tag: this.tag,
38323             cls: 'blog-masonary-wrapper ' + this.cls,
38324             cn : {
38325                 cls : 'mas-boxes masonary'
38326             }
38327         };
38328         
38329         return cfg;
38330     },
38331     
38332     getChildContainer: function( )
38333     {
38334         if (this.boxesEl) {
38335             return this.boxesEl;
38336         }
38337         
38338         this.boxesEl = this.el.select('.mas-boxes').first();
38339         
38340         return this.boxesEl;
38341     },
38342     
38343     
38344     initEvents : function()
38345     {
38346         var _this = this;
38347         
38348         if(this.isAutoInitial){
38349             Roo.log('hook children rendered');
38350             this.on('childrenrendered', function() {
38351                 Roo.log('children rendered');
38352                 _this.initial();
38353             } ,this);
38354         }
38355     },
38356     
38357     initial : function()
38358     {
38359         this.selectedBrick = [];
38360         
38361         this.currentSize = this.el.getBox(true);
38362         
38363         Roo.EventManager.onWindowResize(this.resize, this); 
38364
38365         if(!this.isAutoInitial){
38366             this.layout();
38367             return;
38368         }
38369         
38370         this.layout();
38371         
38372         return;
38373         //this.layout.defer(500,this);
38374         
38375     },
38376     
38377     resize : function()
38378     {
38379         var cs = this.el.getBox(true);
38380         
38381         if (
38382                 this.currentSize.width == cs.width && 
38383                 this.currentSize.x == cs.x && 
38384                 this.currentSize.height == cs.height && 
38385                 this.currentSize.y == cs.y 
38386         ) {
38387             Roo.log("no change in with or X or Y");
38388             return;
38389         }
38390         
38391         this.currentSize = cs;
38392         
38393         this.layout();
38394         
38395     },
38396     
38397     layout : function()
38398     {   
38399         this._resetLayout();
38400         
38401         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38402         
38403         this.layoutItems( isInstant );
38404       
38405         this._isLayoutInited = true;
38406         
38407         this.fireEvent('layout', this);
38408         
38409     },
38410     
38411     _resetLayout : function()
38412     {
38413         if(this.isHorizontal){
38414             this.horizontalMeasureColumns();
38415             return;
38416         }
38417         
38418         this.verticalMeasureColumns();
38419         
38420     },
38421     
38422     verticalMeasureColumns : function()
38423     {
38424         this.getContainerWidth();
38425         
38426 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38427 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
38428 //            return;
38429 //        }
38430         
38431         var boxWidth = this.boxWidth + this.padWidth;
38432         
38433         if(this.containerWidth < this.boxWidth){
38434             boxWidth = this.containerWidth
38435         }
38436         
38437         var containerWidth = this.containerWidth;
38438         
38439         var cols = Math.floor(containerWidth / boxWidth);
38440         
38441         this.cols = Math.max( cols, 1 );
38442         
38443         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38444         
38445         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
38446         
38447         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
38448         
38449         this.colWidth = boxWidth + avail - this.padWidth;
38450         
38451         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
38452         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
38453     },
38454     
38455     horizontalMeasureColumns : function()
38456     {
38457         this.getContainerWidth();
38458         
38459         var boxWidth = this.boxWidth;
38460         
38461         if(this.containerWidth < boxWidth){
38462             boxWidth = this.containerWidth;
38463         }
38464         
38465         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
38466         
38467         this.el.setHeight(boxWidth);
38468         
38469     },
38470     
38471     getContainerWidth : function()
38472     {
38473         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38474     },
38475     
38476     layoutItems : function( isInstant )
38477     {
38478         Roo.log(this.bricks);
38479         
38480         var items = Roo.apply([], this.bricks);
38481         
38482         if(this.isHorizontal){
38483             this._horizontalLayoutItems( items , isInstant );
38484             return;
38485         }
38486         
38487 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38488 //            this._verticalAlternativeLayoutItems( items , isInstant );
38489 //            return;
38490 //        }
38491         
38492         this._verticalLayoutItems( items , isInstant );
38493         
38494     },
38495     
38496     _verticalLayoutItems : function ( items , isInstant)
38497     {
38498         if ( !items || !items.length ) {
38499             return;
38500         }
38501         
38502         var standard = [
38503             ['xs', 'xs', 'xs', 'tall'],
38504             ['xs', 'xs', 'tall'],
38505             ['xs', 'xs', 'sm'],
38506             ['xs', 'xs', 'xs'],
38507             ['xs', 'tall'],
38508             ['xs', 'sm'],
38509             ['xs', 'xs'],
38510             ['xs'],
38511             
38512             ['sm', 'xs', 'xs'],
38513             ['sm', 'xs'],
38514             ['sm'],
38515             
38516             ['tall', 'xs', 'xs', 'xs'],
38517             ['tall', 'xs', 'xs'],
38518             ['tall', 'xs'],
38519             ['tall']
38520             
38521         ];
38522         
38523         var queue = [];
38524         
38525         var boxes = [];
38526         
38527         var box = [];
38528         
38529         Roo.each(items, function(item, k){
38530             
38531             switch (item.size) {
38532                 // these layouts take up a full box,
38533                 case 'md' :
38534                 case 'md-left' :
38535                 case 'md-right' :
38536                 case 'wide' :
38537                     
38538                     if(box.length){
38539                         boxes.push(box);
38540                         box = [];
38541                     }
38542                     
38543                     boxes.push([item]);
38544                     
38545                     break;
38546                     
38547                 case 'xs' :
38548                 case 'sm' :
38549                 case 'tall' :
38550                     
38551                     box.push(item);
38552                     
38553                     break;
38554                 default :
38555                     break;
38556                     
38557             }
38558             
38559         }, this);
38560         
38561         if(box.length){
38562             boxes.push(box);
38563             box = [];
38564         }
38565         
38566         var filterPattern = function(box, length)
38567         {
38568             if(!box.length){
38569                 return;
38570             }
38571             
38572             var match = false;
38573             
38574             var pattern = box.slice(0, length);
38575             
38576             var format = [];
38577             
38578             Roo.each(pattern, function(i){
38579                 format.push(i.size);
38580             }, this);
38581             
38582             Roo.each(standard, function(s){
38583                 
38584                 if(String(s) != String(format)){
38585                     return;
38586                 }
38587                 
38588                 match = true;
38589                 return false;
38590                 
38591             }, this);
38592             
38593             if(!match && length == 1){
38594                 return;
38595             }
38596             
38597             if(!match){
38598                 filterPattern(box, length - 1);
38599                 return;
38600             }
38601                 
38602             queue.push(pattern);
38603
38604             box = box.slice(length, box.length);
38605
38606             filterPattern(box, 4);
38607
38608             return;
38609             
38610         }
38611         
38612         Roo.each(boxes, function(box, k){
38613             
38614             if(!box.length){
38615                 return;
38616             }
38617             
38618             if(box.length == 1){
38619                 queue.push(box);
38620                 return;
38621             }
38622             
38623             filterPattern(box, 4);
38624             
38625         }, this);
38626         
38627         this._processVerticalLayoutQueue( queue, isInstant );
38628         
38629     },
38630     
38631 //    _verticalAlternativeLayoutItems : function( items , isInstant )
38632 //    {
38633 //        if ( !items || !items.length ) {
38634 //            return;
38635 //        }
38636 //
38637 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
38638 //        
38639 //    },
38640     
38641     _horizontalLayoutItems : function ( items , isInstant)
38642     {
38643         if ( !items || !items.length || items.length < 3) {
38644             return;
38645         }
38646         
38647         items.reverse();
38648         
38649         var eItems = items.slice(0, 3);
38650         
38651         items = items.slice(3, items.length);
38652         
38653         var standard = [
38654             ['xs', 'xs', 'xs', 'wide'],
38655             ['xs', 'xs', 'wide'],
38656             ['xs', 'xs', 'sm'],
38657             ['xs', 'xs', 'xs'],
38658             ['xs', 'wide'],
38659             ['xs', 'sm'],
38660             ['xs', 'xs'],
38661             ['xs'],
38662             
38663             ['sm', 'xs', 'xs'],
38664             ['sm', 'xs'],
38665             ['sm'],
38666             
38667             ['wide', 'xs', 'xs', 'xs'],
38668             ['wide', 'xs', 'xs'],
38669             ['wide', 'xs'],
38670             ['wide'],
38671             
38672             ['wide-thin']
38673         ];
38674         
38675         var queue = [];
38676         
38677         var boxes = [];
38678         
38679         var box = [];
38680         
38681         Roo.each(items, function(item, k){
38682             
38683             switch (item.size) {
38684                 case 'md' :
38685                 case 'md-left' :
38686                 case 'md-right' :
38687                 case 'tall' :
38688                     
38689                     if(box.length){
38690                         boxes.push(box);
38691                         box = [];
38692                     }
38693                     
38694                     boxes.push([item]);
38695                     
38696                     break;
38697                     
38698                 case 'xs' :
38699                 case 'sm' :
38700                 case 'wide' :
38701                 case 'wide-thin' :
38702                     
38703                     box.push(item);
38704                     
38705                     break;
38706                 default :
38707                     break;
38708                     
38709             }
38710             
38711         }, this);
38712         
38713         if(box.length){
38714             boxes.push(box);
38715             box = [];
38716         }
38717         
38718         var filterPattern = function(box, length)
38719         {
38720             if(!box.length){
38721                 return;
38722             }
38723             
38724             var match = false;
38725             
38726             var pattern = box.slice(0, length);
38727             
38728             var format = [];
38729             
38730             Roo.each(pattern, function(i){
38731                 format.push(i.size);
38732             }, this);
38733             
38734             Roo.each(standard, function(s){
38735                 
38736                 if(String(s) != String(format)){
38737                     return;
38738                 }
38739                 
38740                 match = true;
38741                 return false;
38742                 
38743             }, this);
38744             
38745             if(!match && length == 1){
38746                 return;
38747             }
38748             
38749             if(!match){
38750                 filterPattern(box, length - 1);
38751                 return;
38752             }
38753                 
38754             queue.push(pattern);
38755
38756             box = box.slice(length, box.length);
38757
38758             filterPattern(box, 4);
38759
38760             return;
38761             
38762         }
38763         
38764         Roo.each(boxes, function(box, k){
38765             
38766             if(!box.length){
38767                 return;
38768             }
38769             
38770             if(box.length == 1){
38771                 queue.push(box);
38772                 return;
38773             }
38774             
38775             filterPattern(box, 4);
38776             
38777         }, this);
38778         
38779         
38780         var prune = [];
38781         
38782         var pos = this.el.getBox(true);
38783         
38784         var minX = pos.x;
38785         
38786         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38787         
38788         var hit_end = false;
38789         
38790         Roo.each(queue, function(box){
38791             
38792             if(hit_end){
38793                 
38794                 Roo.each(box, function(b){
38795                 
38796                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
38797                     b.el.hide();
38798
38799                 }, this);
38800
38801                 return;
38802             }
38803             
38804             var mx = 0;
38805             
38806             Roo.each(box, function(b){
38807                 
38808                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
38809                 b.el.show();
38810
38811                 mx = Math.max(mx, b.x);
38812                 
38813             }, this);
38814             
38815             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
38816             
38817             if(maxX < minX){
38818                 
38819                 Roo.each(box, function(b){
38820                 
38821                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
38822                     b.el.hide();
38823                     
38824                 }, this);
38825                 
38826                 hit_end = true;
38827                 
38828                 return;
38829             }
38830             
38831             prune.push(box);
38832             
38833         }, this);
38834         
38835         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
38836     },
38837     
38838     /** Sets position of item in DOM
38839     * @param {Element} item
38840     * @param {Number} x - horizontal position
38841     * @param {Number} y - vertical position
38842     * @param {Boolean} isInstant - disables transitions
38843     */
38844     _processVerticalLayoutQueue : function( queue, isInstant )
38845     {
38846         var pos = this.el.getBox(true);
38847         var x = pos.x;
38848         var y = pos.y;
38849         var maxY = [];
38850         
38851         for (var i = 0; i < this.cols; i++){
38852             maxY[i] = pos.y;
38853         }
38854         
38855         Roo.each(queue, function(box, k){
38856             
38857             var col = k % this.cols;
38858             
38859             Roo.each(box, function(b,kk){
38860                 
38861                 b.el.position('absolute');
38862                 
38863                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38864                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
38865                 
38866                 if(b.size == 'md-left' || b.size == 'md-right'){
38867                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
38868                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
38869                 }
38870                 
38871                 b.el.setWidth(width);
38872                 b.el.setHeight(height);
38873                 // iframe?
38874                 b.el.select('iframe',true).setSize(width,height);
38875                 
38876             }, this);
38877             
38878             for (var i = 0; i < this.cols; i++){
38879                 
38880                 if(maxY[i] < maxY[col]){
38881                     col = i;
38882                     continue;
38883                 }
38884                 
38885                 col = Math.min(col, i);
38886                 
38887             }
38888             
38889             x = pos.x + col * (this.colWidth + this.padWidth);
38890             
38891             y = maxY[col];
38892             
38893             var positions = [];
38894             
38895             switch (box.length){
38896                 case 1 :
38897                     positions = this.getVerticalOneBoxColPositions(x, y, box);
38898                     break;
38899                 case 2 :
38900                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
38901                     break;
38902                 case 3 :
38903                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
38904                     break;
38905                 case 4 :
38906                     positions = this.getVerticalFourBoxColPositions(x, y, box);
38907                     break;
38908                 default :
38909                     break;
38910             }
38911             
38912             Roo.each(box, function(b,kk){
38913                 
38914                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
38915                 
38916                 var sz = b.el.getSize();
38917                 
38918                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
38919                 
38920             }, this);
38921             
38922         }, this);
38923         
38924         var mY = 0;
38925         
38926         for (var i = 0; i < this.cols; i++){
38927             mY = Math.max(mY, maxY[i]);
38928         }
38929         
38930         this.el.setHeight(mY - pos.y);
38931         
38932     },
38933     
38934 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
38935 //    {
38936 //        var pos = this.el.getBox(true);
38937 //        var x = pos.x;
38938 //        var y = pos.y;
38939 //        var maxX = pos.right;
38940 //        
38941 //        var maxHeight = 0;
38942 //        
38943 //        Roo.each(items, function(item, k){
38944 //            
38945 //            var c = k % 2;
38946 //            
38947 //            item.el.position('absolute');
38948 //                
38949 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
38950 //
38951 //            item.el.setWidth(width);
38952 //
38953 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
38954 //
38955 //            item.el.setHeight(height);
38956 //            
38957 //            if(c == 0){
38958 //                item.el.setXY([x, y], isInstant ? false : true);
38959 //            } else {
38960 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
38961 //            }
38962 //            
38963 //            y = y + height + this.alternativePadWidth;
38964 //            
38965 //            maxHeight = maxHeight + height + this.alternativePadWidth;
38966 //            
38967 //        }, this);
38968 //        
38969 //        this.el.setHeight(maxHeight);
38970 //        
38971 //    },
38972     
38973     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
38974     {
38975         var pos = this.el.getBox(true);
38976         
38977         var minX = pos.x;
38978         var minY = pos.y;
38979         
38980         var maxX = pos.right;
38981         
38982         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
38983         
38984         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38985         
38986         Roo.each(queue, function(box, k){
38987             
38988             Roo.each(box, function(b, kk){
38989                 
38990                 b.el.position('absolute');
38991                 
38992                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38993                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
38994                 
38995                 if(b.size == 'md-left' || b.size == 'md-right'){
38996                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
38997                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
38998                 }
38999                 
39000                 b.el.setWidth(width);
39001                 b.el.setHeight(height);
39002                 
39003             }, this);
39004             
39005             if(!box.length){
39006                 return;
39007             }
39008             
39009             var positions = [];
39010             
39011             switch (box.length){
39012                 case 1 :
39013                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39014                     break;
39015                 case 2 :
39016                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39017                     break;
39018                 case 3 :
39019                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39020                     break;
39021                 case 4 :
39022                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39023                     break;
39024                 default :
39025                     break;
39026             }
39027             
39028             Roo.each(box, function(b,kk){
39029                 
39030                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39031                 
39032                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39033                 
39034             }, this);
39035             
39036         }, this);
39037         
39038     },
39039     
39040     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39041     {
39042         Roo.each(eItems, function(b,k){
39043             
39044             b.size = (k == 0) ? 'sm' : 'xs';
39045             b.x = (k == 0) ? 2 : 1;
39046             b.y = (k == 0) ? 2 : 1;
39047             
39048             b.el.position('absolute');
39049             
39050             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39051                 
39052             b.el.setWidth(width);
39053             
39054             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39055             
39056             b.el.setHeight(height);
39057             
39058         }, this);
39059
39060         var positions = [];
39061         
39062         positions.push({
39063             x : maxX - this.unitWidth * 2 - this.gutter,
39064             y : minY
39065         });
39066         
39067         positions.push({
39068             x : maxX - this.unitWidth,
39069             y : minY + (this.unitWidth + this.gutter) * 2
39070         });
39071         
39072         positions.push({
39073             x : maxX - this.unitWidth * 3 - this.gutter * 2,
39074             y : minY
39075         });
39076         
39077         Roo.each(eItems, function(b,k){
39078             
39079             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39080
39081         }, this);
39082         
39083     },
39084     
39085     getVerticalOneBoxColPositions : function(x, y, box)
39086     {
39087         var pos = [];
39088         
39089         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39090         
39091         if(box[0].size == 'md-left'){
39092             rand = 0;
39093         }
39094         
39095         if(box[0].size == 'md-right'){
39096             rand = 1;
39097         }
39098         
39099         pos.push({
39100             x : x + (this.unitWidth + this.gutter) * rand,
39101             y : y
39102         });
39103         
39104         return pos;
39105     },
39106     
39107     getVerticalTwoBoxColPositions : function(x, y, box)
39108     {
39109         var pos = [];
39110         
39111         if(box[0].size == 'xs'){
39112             
39113             pos.push({
39114                 x : x,
39115                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39116             });
39117
39118             pos.push({
39119                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39120                 y : y
39121             });
39122             
39123             return pos;
39124             
39125         }
39126         
39127         pos.push({
39128             x : x,
39129             y : y
39130         });
39131
39132         pos.push({
39133             x : x + (this.unitWidth + this.gutter) * 2,
39134             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39135         });
39136         
39137         return pos;
39138         
39139     },
39140     
39141     getVerticalThreeBoxColPositions : function(x, y, box)
39142     {
39143         var pos = [];
39144         
39145         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39146             
39147             pos.push({
39148                 x : x,
39149                 y : y
39150             });
39151
39152             pos.push({
39153                 x : x + (this.unitWidth + this.gutter) * 1,
39154                 y : y
39155             });
39156             
39157             pos.push({
39158                 x : x + (this.unitWidth + this.gutter) * 2,
39159                 y : y
39160             });
39161             
39162             return pos;
39163             
39164         }
39165         
39166         if(box[0].size == 'xs' && box[1].size == 'xs'){
39167             
39168             pos.push({
39169                 x : x,
39170                 y : y
39171             });
39172
39173             pos.push({
39174                 x : x,
39175                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
39176             });
39177             
39178             pos.push({
39179                 x : x + (this.unitWidth + this.gutter) * 1,
39180                 y : y
39181             });
39182             
39183             return pos;
39184             
39185         }
39186         
39187         pos.push({
39188             x : x,
39189             y : y
39190         });
39191
39192         pos.push({
39193             x : x + (this.unitWidth + this.gutter) * 2,
39194             y : y
39195         });
39196
39197         pos.push({
39198             x : x + (this.unitWidth + this.gutter) * 2,
39199             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
39200         });
39201             
39202         return pos;
39203         
39204     },
39205     
39206     getVerticalFourBoxColPositions : function(x, y, box)
39207     {
39208         var pos = [];
39209         
39210         if(box[0].size == 'xs'){
39211             
39212             pos.push({
39213                 x : x,
39214                 y : y
39215             });
39216
39217             pos.push({
39218                 x : x,
39219                 y : y + (this.unitHeight + this.gutter) * 1
39220             });
39221             
39222             pos.push({
39223                 x : x,
39224                 y : y + (this.unitHeight + this.gutter) * 2
39225             });
39226             
39227             pos.push({
39228                 x : x + (this.unitWidth + this.gutter) * 1,
39229                 y : y
39230             });
39231             
39232             return pos;
39233             
39234         }
39235         
39236         pos.push({
39237             x : x,
39238             y : y
39239         });
39240
39241         pos.push({
39242             x : x + (this.unitWidth + this.gutter) * 2,
39243             y : y
39244         });
39245
39246         pos.push({
39247             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
39248             y : y + (this.unitHeight + this.gutter) * 1
39249         });
39250
39251         pos.push({
39252             x : x + (this.unitWidth + this.gutter) * 2,
39253             y : y + (this.unitWidth + this.gutter) * 2
39254         });
39255
39256         return pos;
39257         
39258     },
39259     
39260     getHorizontalOneBoxColPositions : function(maxX, minY, box)
39261     {
39262         var pos = [];
39263         
39264         if(box[0].size == 'md-left'){
39265             pos.push({
39266                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39267                 y : minY
39268             });
39269             
39270             return pos;
39271         }
39272         
39273         if(box[0].size == 'md-right'){
39274             pos.push({
39275                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39276                 y : minY + (this.unitWidth + this.gutter) * 1
39277             });
39278             
39279             return pos;
39280         }
39281         
39282         var rand = Math.floor(Math.random() * (4 - box[0].y));
39283         
39284         pos.push({
39285             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39286             y : minY + (this.unitWidth + this.gutter) * rand
39287         });
39288         
39289         return pos;
39290         
39291     },
39292     
39293     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
39294     {
39295         var pos = [];
39296         
39297         if(box[0].size == 'xs'){
39298             
39299             pos.push({
39300                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39301                 y : minY
39302             });
39303
39304             pos.push({
39305                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39306                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
39307             });
39308             
39309             return pos;
39310             
39311         }
39312         
39313         pos.push({
39314             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39315             y : minY
39316         });
39317
39318         pos.push({
39319             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39320             y : minY + (this.unitWidth + this.gutter) * 2
39321         });
39322         
39323         return pos;
39324         
39325     },
39326     
39327     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
39328     {
39329         var pos = [];
39330         
39331         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39332             
39333             pos.push({
39334                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39335                 y : minY
39336             });
39337
39338             pos.push({
39339                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39340                 y : minY + (this.unitWidth + this.gutter) * 1
39341             });
39342             
39343             pos.push({
39344                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39345                 y : minY + (this.unitWidth + this.gutter) * 2
39346             });
39347             
39348             return pos;
39349             
39350         }
39351         
39352         if(box[0].size == 'xs' && box[1].size == 'xs'){
39353             
39354             pos.push({
39355                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39356                 y : minY
39357             });
39358
39359             pos.push({
39360                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39361                 y : minY
39362             });
39363             
39364             pos.push({
39365                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39366                 y : minY + (this.unitWidth + this.gutter) * 1
39367             });
39368             
39369             return pos;
39370             
39371         }
39372         
39373         pos.push({
39374             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39375             y : minY
39376         });
39377
39378         pos.push({
39379             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39380             y : minY + (this.unitWidth + this.gutter) * 2
39381         });
39382
39383         pos.push({
39384             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39385             y : minY + (this.unitWidth + this.gutter) * 2
39386         });
39387             
39388         return pos;
39389         
39390     },
39391     
39392     getHorizontalFourBoxColPositions : function(maxX, minY, box)
39393     {
39394         var pos = [];
39395         
39396         if(box[0].size == 'xs'){
39397             
39398             pos.push({
39399                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39400                 y : minY
39401             });
39402
39403             pos.push({
39404                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39405                 y : minY
39406             });
39407             
39408             pos.push({
39409                 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),
39410                 y : minY
39411             });
39412             
39413             pos.push({
39414                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
39415                 y : minY + (this.unitWidth + this.gutter) * 1
39416             });
39417             
39418             return pos;
39419             
39420         }
39421         
39422         pos.push({
39423             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39424             y : minY
39425         });
39426         
39427         pos.push({
39428             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39429             y : minY + (this.unitWidth + this.gutter) * 2
39430         });
39431         
39432         pos.push({
39433             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39434             y : minY + (this.unitWidth + this.gutter) * 2
39435         });
39436         
39437         pos.push({
39438             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),
39439             y : minY + (this.unitWidth + this.gutter) * 2
39440         });
39441
39442         return pos;
39443         
39444     },
39445     
39446     /**
39447     * remove a Masonry Brick
39448     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
39449     */
39450     removeBrick : function(brick_id)
39451     {
39452         if (!brick_id) {
39453             return;
39454         }
39455         
39456         for (var i = 0; i<this.bricks.length; i++) {
39457             if (this.bricks[i].id == brick_id) {
39458                 this.bricks.splice(i,1);
39459                 this.el.dom.removeChild(Roo.get(brick_id).dom);
39460                 this.initial();
39461             }
39462         }
39463     },
39464     
39465     /**
39466     * adds a Masonry Brick
39467     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39468     */
39469     addBrick : function(cfg)
39470     {
39471         var cn = new Roo.bootstrap.MasonryBrick(cfg);
39472         //this.register(cn);
39473         cn.parentId = this.id;
39474         cn.render(this.el);
39475         return cn;
39476     },
39477     
39478     /**
39479     * register a Masonry Brick
39480     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39481     */
39482     
39483     register : function(brick)
39484     {
39485         this.bricks.push(brick);
39486         brick.masonryId = this.id;
39487     },
39488     
39489     /**
39490     * clear all the Masonry Brick
39491     */
39492     clearAll : function()
39493     {
39494         this.bricks = [];
39495         //this.getChildContainer().dom.innerHTML = "";
39496         this.el.dom.innerHTML = '';
39497     },
39498     
39499     getSelected : function()
39500     {
39501         if (!this.selectedBrick) {
39502             return false;
39503         }
39504         
39505         return this.selectedBrick;
39506     }
39507 });
39508
39509 Roo.apply(Roo.bootstrap.LayoutMasonry, {
39510     
39511     groups: {},
39512      /**
39513     * register a Masonry Layout
39514     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
39515     */
39516     
39517     register : function(layout)
39518     {
39519         this.groups[layout.id] = layout;
39520     },
39521     /**
39522     * fetch a  Masonry Layout based on the masonry layout ID
39523     * @param {string} the masonry layout to add
39524     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
39525     */
39526     
39527     get: function(layout_id) {
39528         if (typeof(this.groups[layout_id]) == 'undefined') {
39529             return false;
39530         }
39531         return this.groups[layout_id] ;
39532     }
39533     
39534     
39535     
39536 });
39537
39538  
39539
39540  /**
39541  *
39542  * This is based on 
39543  * http://masonry.desandro.com
39544  *
39545  * The idea is to render all the bricks based on vertical width...
39546  *
39547  * The original code extends 'outlayer' - we might need to use that....
39548  * 
39549  */
39550
39551
39552 /**
39553  * @class Roo.bootstrap.LayoutMasonryAuto
39554  * @extends Roo.bootstrap.Component
39555  * Bootstrap Layout Masonry class
39556  * 
39557  * @constructor
39558  * Create a new Element
39559  * @param {Object} config The config object
39560  */
39561
39562 Roo.bootstrap.LayoutMasonryAuto = function(config){
39563     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
39564 };
39565
39566 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
39567     
39568       /**
39569      * @cfg {Boolean} isFitWidth  - resize the width..
39570      */   
39571     isFitWidth : false,  // options..
39572     /**
39573      * @cfg {Boolean} isOriginLeft = left align?
39574      */   
39575     isOriginLeft : true,
39576     /**
39577      * @cfg {Boolean} isOriginTop = top align?
39578      */   
39579     isOriginTop : false,
39580     /**
39581      * @cfg {Boolean} isLayoutInstant = no animation?
39582      */   
39583     isLayoutInstant : false, // needed?
39584     /**
39585      * @cfg {Boolean} isResizingContainer = not sure if this is used..
39586      */   
39587     isResizingContainer : true,
39588     /**
39589      * @cfg {Number} columnWidth  width of the columns 
39590      */   
39591     
39592     columnWidth : 0,
39593     
39594     /**
39595      * @cfg {Number} maxCols maximum number of columns
39596      */   
39597     
39598     maxCols: 0,
39599     /**
39600      * @cfg {Number} padHeight padding below box..
39601      */   
39602     
39603     padHeight : 10, 
39604     
39605     /**
39606      * @cfg {Boolean} isAutoInitial defalut true
39607      */   
39608     
39609     isAutoInitial : true, 
39610     
39611     // private?
39612     gutter : 0,
39613     
39614     containerWidth: 0,
39615     initialColumnWidth : 0,
39616     currentSize : null,
39617     
39618     colYs : null, // array.
39619     maxY : 0,
39620     padWidth: 10,
39621     
39622     
39623     tag: 'div',
39624     cls: '',
39625     bricks: null, //CompositeElement
39626     cols : 0, // array?
39627     // element : null, // wrapped now this.el
39628     _isLayoutInited : null, 
39629     
39630     
39631     getAutoCreate : function(){
39632         
39633         var cfg = {
39634             tag: this.tag,
39635             cls: 'blog-masonary-wrapper ' + this.cls,
39636             cn : {
39637                 cls : 'mas-boxes masonary'
39638             }
39639         };
39640         
39641         return cfg;
39642     },
39643     
39644     getChildContainer: function( )
39645     {
39646         if (this.boxesEl) {
39647             return this.boxesEl;
39648         }
39649         
39650         this.boxesEl = this.el.select('.mas-boxes').first();
39651         
39652         return this.boxesEl;
39653     },
39654     
39655     
39656     initEvents : function()
39657     {
39658         var _this = this;
39659         
39660         if(this.isAutoInitial){
39661             Roo.log('hook children rendered');
39662             this.on('childrenrendered', function() {
39663                 Roo.log('children rendered');
39664                 _this.initial();
39665             } ,this);
39666         }
39667         
39668     },
39669     
39670     initial : function()
39671     {
39672         this.reloadItems();
39673
39674         this.currentSize = this.el.getBox(true);
39675
39676         /// was window resize... - let's see if this works..
39677         Roo.EventManager.onWindowResize(this.resize, this); 
39678
39679         if(!this.isAutoInitial){
39680             this.layout();
39681             return;
39682         }
39683         
39684         this.layout.defer(500,this);
39685     },
39686     
39687     reloadItems: function()
39688     {
39689         this.bricks = this.el.select('.masonry-brick', true);
39690         
39691         this.bricks.each(function(b) {
39692             //Roo.log(b.getSize());
39693             if (!b.attr('originalwidth')) {
39694                 b.attr('originalwidth',  b.getSize().width);
39695             }
39696             
39697         });
39698         
39699         Roo.log(this.bricks.elements.length);
39700     },
39701     
39702     resize : function()
39703     {
39704         Roo.log('resize');
39705         var cs = this.el.getBox(true);
39706         
39707         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
39708             Roo.log("no change in with or X");
39709             return;
39710         }
39711         this.currentSize = cs;
39712         this.layout();
39713     },
39714     
39715     layout : function()
39716     {
39717          Roo.log('layout');
39718         this._resetLayout();
39719         //this._manageStamps();
39720       
39721         // don't animate first layout
39722         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
39723         this.layoutItems( isInstant );
39724       
39725         // flag for initalized
39726         this._isLayoutInited = true;
39727     },
39728     
39729     layoutItems : function( isInstant )
39730     {
39731         //var items = this._getItemsForLayout( this.items );
39732         // original code supports filtering layout items.. we just ignore it..
39733         
39734         this._layoutItems( this.bricks , isInstant );
39735       
39736         this._postLayout();
39737     },
39738     _layoutItems : function ( items , isInstant)
39739     {
39740        //this.fireEvent( 'layout', this, items );
39741     
39742
39743         if ( !items || !items.elements.length ) {
39744           // no items, emit event with empty array
39745             return;
39746         }
39747
39748         var queue = [];
39749         items.each(function(item) {
39750             Roo.log("layout item");
39751             Roo.log(item);
39752             // get x/y object from method
39753             var position = this._getItemLayoutPosition( item );
39754             // enqueue
39755             position.item = item;
39756             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
39757             queue.push( position );
39758         }, this);
39759       
39760         this._processLayoutQueue( queue );
39761     },
39762     /** Sets position of item in DOM
39763     * @param {Element} item
39764     * @param {Number} x - horizontal position
39765     * @param {Number} y - vertical position
39766     * @param {Boolean} isInstant - disables transitions
39767     */
39768     _processLayoutQueue : function( queue )
39769     {
39770         for ( var i=0, len = queue.length; i < len; i++ ) {
39771             var obj = queue[i];
39772             obj.item.position('absolute');
39773             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
39774         }
39775     },
39776       
39777     
39778     /**
39779     * Any logic you want to do after each layout,
39780     * i.e. size the container
39781     */
39782     _postLayout : function()
39783     {
39784         this.resizeContainer();
39785     },
39786     
39787     resizeContainer : function()
39788     {
39789         if ( !this.isResizingContainer ) {
39790             return;
39791         }
39792         var size = this._getContainerSize();
39793         if ( size ) {
39794             this.el.setSize(size.width,size.height);
39795             this.boxesEl.setSize(size.width,size.height);
39796         }
39797     },
39798     
39799     
39800     
39801     _resetLayout : function()
39802     {
39803         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
39804         this.colWidth = this.el.getWidth();
39805         //this.gutter = this.el.getWidth(); 
39806         
39807         this.measureColumns();
39808
39809         // reset column Y
39810         var i = this.cols;
39811         this.colYs = [];
39812         while (i--) {
39813             this.colYs.push( 0 );
39814         }
39815     
39816         this.maxY = 0;
39817     },
39818
39819     measureColumns : function()
39820     {
39821         this.getContainerWidth();
39822       // if columnWidth is 0, default to outerWidth of first item
39823         if ( !this.columnWidth ) {
39824             var firstItem = this.bricks.first();
39825             Roo.log(firstItem);
39826             this.columnWidth  = this.containerWidth;
39827             if (firstItem && firstItem.attr('originalwidth') ) {
39828                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
39829             }
39830             // columnWidth fall back to item of first element
39831             Roo.log("set column width?");
39832                         this.initialColumnWidth = this.columnWidth  ;
39833
39834             // if first elem has no width, default to size of container
39835             
39836         }
39837         
39838         
39839         if (this.initialColumnWidth) {
39840             this.columnWidth = this.initialColumnWidth;
39841         }
39842         
39843         
39844             
39845         // column width is fixed at the top - however if container width get's smaller we should
39846         // reduce it...
39847         
39848         // this bit calcs how man columns..
39849             
39850         var columnWidth = this.columnWidth += this.gutter;
39851       
39852         // calculate columns
39853         var containerWidth = this.containerWidth + this.gutter;
39854         
39855         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
39856         // fix rounding errors, typically with gutters
39857         var excess = columnWidth - containerWidth % columnWidth;
39858         
39859         
39860         // if overshoot is less than a pixel, round up, otherwise floor it
39861         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
39862         cols = Math[ mathMethod ]( cols );
39863         this.cols = Math.max( cols, 1 );
39864         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39865         
39866          // padding positioning..
39867         var totalColWidth = this.cols * this.columnWidth;
39868         var padavail = this.containerWidth - totalColWidth;
39869         // so for 2 columns - we need 3 'pads'
39870         
39871         var padNeeded = (1+this.cols) * this.padWidth;
39872         
39873         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
39874         
39875         this.columnWidth += padExtra
39876         //this.padWidth = Math.floor(padavail /  ( this.cols));
39877         
39878         // adjust colum width so that padding is fixed??
39879         
39880         // we have 3 columns ... total = width * 3
39881         // we have X left over... that should be used by 
39882         
39883         //if (this.expandC) {
39884             
39885         //}
39886         
39887         
39888         
39889     },
39890     
39891     getContainerWidth : function()
39892     {
39893        /* // container is parent if fit width
39894         var container = this.isFitWidth ? this.element.parentNode : this.element;
39895         // check that this.size and size are there
39896         // IE8 triggers resize on body size change, so they might not be
39897         
39898         var size = getSize( container );  //FIXME
39899         this.containerWidth = size && size.innerWidth; //FIXME
39900         */
39901          
39902         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
39903         
39904     },
39905     
39906     _getItemLayoutPosition : function( item )  // what is item?
39907     {
39908         // we resize the item to our columnWidth..
39909       
39910         item.setWidth(this.columnWidth);
39911         item.autoBoxAdjust  = false;
39912         
39913         var sz = item.getSize();
39914  
39915         // how many columns does this brick span
39916         var remainder = this.containerWidth % this.columnWidth;
39917         
39918         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
39919         // round if off by 1 pixel, otherwise use ceil
39920         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
39921         colSpan = Math.min( colSpan, this.cols );
39922         
39923         // normally this should be '1' as we dont' currently allow multi width columns..
39924         
39925         var colGroup = this._getColGroup( colSpan );
39926         // get the minimum Y value from the columns
39927         var minimumY = Math.min.apply( Math, colGroup );
39928         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
39929         
39930         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
39931          
39932         // position the brick
39933         var position = {
39934             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
39935             y: this.currentSize.y + minimumY + this.padHeight
39936         };
39937         
39938         Roo.log(position);
39939         // apply setHeight to necessary columns
39940         var setHeight = minimumY + sz.height + this.padHeight;
39941         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
39942         
39943         var setSpan = this.cols + 1 - colGroup.length;
39944         for ( var i = 0; i < setSpan; i++ ) {
39945           this.colYs[ shortColIndex + i ] = setHeight ;
39946         }
39947       
39948         return position;
39949     },
39950     
39951     /**
39952      * @param {Number} colSpan - number of columns the element spans
39953      * @returns {Array} colGroup
39954      */
39955     _getColGroup : function( colSpan )
39956     {
39957         if ( colSpan < 2 ) {
39958           // if brick spans only one column, use all the column Ys
39959           return this.colYs;
39960         }
39961       
39962         var colGroup = [];
39963         // how many different places could this brick fit horizontally
39964         var groupCount = this.cols + 1 - colSpan;
39965         // for each group potential horizontal position
39966         for ( var i = 0; i < groupCount; i++ ) {
39967           // make an array of colY values for that one group
39968           var groupColYs = this.colYs.slice( i, i + colSpan );
39969           // and get the max value of the array
39970           colGroup[i] = Math.max.apply( Math, groupColYs );
39971         }
39972         return colGroup;
39973     },
39974     /*
39975     _manageStamp : function( stamp )
39976     {
39977         var stampSize =  stamp.getSize();
39978         var offset = stamp.getBox();
39979         // get the columns that this stamp affects
39980         var firstX = this.isOriginLeft ? offset.x : offset.right;
39981         var lastX = firstX + stampSize.width;
39982         var firstCol = Math.floor( firstX / this.columnWidth );
39983         firstCol = Math.max( 0, firstCol );
39984         
39985         var lastCol = Math.floor( lastX / this.columnWidth );
39986         // lastCol should not go over if multiple of columnWidth #425
39987         lastCol -= lastX % this.columnWidth ? 0 : 1;
39988         lastCol = Math.min( this.cols - 1, lastCol );
39989         
39990         // set colYs to bottom of the stamp
39991         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
39992             stampSize.height;
39993             
39994         for ( var i = firstCol; i <= lastCol; i++ ) {
39995           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
39996         }
39997     },
39998     */
39999     
40000     _getContainerSize : function()
40001     {
40002         this.maxY = Math.max.apply( Math, this.colYs );
40003         var size = {
40004             height: this.maxY
40005         };
40006       
40007         if ( this.isFitWidth ) {
40008             size.width = this._getContainerFitWidth();
40009         }
40010       
40011         return size;
40012     },
40013     
40014     _getContainerFitWidth : function()
40015     {
40016         var unusedCols = 0;
40017         // count unused columns
40018         var i = this.cols;
40019         while ( --i ) {
40020           if ( this.colYs[i] !== 0 ) {
40021             break;
40022           }
40023           unusedCols++;
40024         }
40025         // fit container to columns that have been used
40026         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40027     },
40028     
40029     needsResizeLayout : function()
40030     {
40031         var previousWidth = this.containerWidth;
40032         this.getContainerWidth();
40033         return previousWidth !== this.containerWidth;
40034     }
40035  
40036 });
40037
40038  
40039
40040  /*
40041  * - LGPL
40042  *
40043  * element
40044  * 
40045  */
40046
40047 /**
40048  * @class Roo.bootstrap.MasonryBrick
40049  * @extends Roo.bootstrap.Component
40050  * Bootstrap MasonryBrick class
40051  * 
40052  * @constructor
40053  * Create a new MasonryBrick
40054  * @param {Object} config The config object
40055  */
40056
40057 Roo.bootstrap.MasonryBrick = function(config){
40058     
40059     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40060     
40061     Roo.bootstrap.MasonryBrick.register(this);
40062     
40063     this.addEvents({
40064         // raw events
40065         /**
40066          * @event click
40067          * When a MasonryBrick is clcik
40068          * @param {Roo.bootstrap.MasonryBrick} this
40069          * @param {Roo.EventObject} e
40070          */
40071         "click" : true
40072     });
40073 };
40074
40075 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
40076     
40077     /**
40078      * @cfg {String} title
40079      */   
40080     title : '',
40081     /**
40082      * @cfg {String} html
40083      */   
40084     html : '',
40085     /**
40086      * @cfg {String} bgimage
40087      */   
40088     bgimage : '',
40089     /**
40090      * @cfg {String} videourl
40091      */   
40092     videourl : '',
40093     /**
40094      * @cfg {String} cls
40095      */   
40096     cls : '',
40097     /**
40098      * @cfg {String} href
40099      */   
40100     href : '',
40101     /**
40102      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40103      */   
40104     size : 'xs',
40105     
40106     /**
40107      * @cfg {String} placetitle (center|bottom)
40108      */   
40109     placetitle : '',
40110     
40111     /**
40112      * @cfg {Boolean} isFitContainer defalut true
40113      */   
40114     isFitContainer : true, 
40115     
40116     /**
40117      * @cfg {Boolean} preventDefault defalut false
40118      */   
40119     preventDefault : false, 
40120     
40121     /**
40122      * @cfg {Boolean} inverse defalut false
40123      */   
40124     maskInverse : false, 
40125     
40126     getAutoCreate : function()
40127     {
40128         if(!this.isFitContainer){
40129             return this.getSplitAutoCreate();
40130         }
40131         
40132         var cls = 'masonry-brick masonry-brick-full';
40133         
40134         if(this.href.length){
40135             cls += ' masonry-brick-link';
40136         }
40137         
40138         if(this.bgimage.length){
40139             cls += ' masonry-brick-image';
40140         }
40141         
40142         if(this.maskInverse){
40143             cls += ' mask-inverse';
40144         }
40145         
40146         if(!this.html.length && !this.maskInverse && !this.videourl.length){
40147             cls += ' enable-mask';
40148         }
40149         
40150         if(this.size){
40151             cls += ' masonry-' + this.size + '-brick';
40152         }
40153         
40154         if(this.placetitle.length){
40155             
40156             switch (this.placetitle) {
40157                 case 'center' :
40158                     cls += ' masonry-center-title';
40159                     break;
40160                 case 'bottom' :
40161                     cls += ' masonry-bottom-title';
40162                     break;
40163                 default:
40164                     break;
40165             }
40166             
40167         } else {
40168             if(!this.html.length && !this.bgimage.length){
40169                 cls += ' masonry-center-title';
40170             }
40171
40172             if(!this.html.length && this.bgimage.length){
40173                 cls += ' masonry-bottom-title';
40174             }
40175         }
40176         
40177         if(this.cls){
40178             cls += ' ' + this.cls;
40179         }
40180         
40181         var cfg = {
40182             tag: (this.href.length) ? 'a' : 'div',
40183             cls: cls,
40184             cn: [
40185                 {
40186                     tag: 'div',
40187                     cls: 'masonry-brick-mask'
40188                 },
40189                 {
40190                     tag: 'div',
40191                     cls: 'masonry-brick-paragraph',
40192                     cn: []
40193                 }
40194             ]
40195         };
40196         
40197         if(this.href.length){
40198             cfg.href = this.href;
40199         }
40200         
40201         var cn = cfg.cn[1].cn;
40202         
40203         if(this.title.length){
40204             cn.push({
40205                 tag: 'h4',
40206                 cls: 'masonry-brick-title',
40207                 html: this.title
40208             });
40209         }
40210         
40211         if(this.html.length){
40212             cn.push({
40213                 tag: 'p',
40214                 cls: 'masonry-brick-text',
40215                 html: this.html
40216             });
40217         }
40218         
40219         if (!this.title.length && !this.html.length) {
40220             cfg.cn[1].cls += ' hide';
40221         }
40222         
40223         if(this.bgimage.length){
40224             cfg.cn.push({
40225                 tag: 'img',
40226                 cls: 'masonry-brick-image-view',
40227                 src: this.bgimage
40228             });
40229         }
40230         
40231         if(this.videourl.length){
40232             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40233             // youtube support only?
40234             cfg.cn.push({
40235                 tag: 'iframe',
40236                 cls: 'masonry-brick-image-view',
40237                 src: vurl,
40238                 frameborder : 0,
40239                 allowfullscreen : true
40240             });
40241         }
40242         
40243         return cfg;
40244         
40245     },
40246     
40247     getSplitAutoCreate : function()
40248     {
40249         var cls = 'masonry-brick masonry-brick-split';
40250         
40251         if(this.href.length){
40252             cls += ' masonry-brick-link';
40253         }
40254         
40255         if(this.bgimage.length){
40256             cls += ' masonry-brick-image';
40257         }
40258         
40259         if(this.size){
40260             cls += ' masonry-' + this.size + '-brick';
40261         }
40262         
40263         switch (this.placetitle) {
40264             case 'center' :
40265                 cls += ' masonry-center-title';
40266                 break;
40267             case 'bottom' :
40268                 cls += ' masonry-bottom-title';
40269                 break;
40270             default:
40271                 if(!this.bgimage.length){
40272                     cls += ' masonry-center-title';
40273                 }
40274
40275                 if(this.bgimage.length){
40276                     cls += ' masonry-bottom-title';
40277                 }
40278                 break;
40279         }
40280         
40281         if(this.cls){
40282             cls += ' ' + this.cls;
40283         }
40284         
40285         var cfg = {
40286             tag: (this.href.length) ? 'a' : 'div',
40287             cls: cls,
40288             cn: [
40289                 {
40290                     tag: 'div',
40291                     cls: 'masonry-brick-split-head',
40292                     cn: [
40293                         {
40294                             tag: 'div',
40295                             cls: 'masonry-brick-paragraph',
40296                             cn: []
40297                         }
40298                     ]
40299                 },
40300                 {
40301                     tag: 'div',
40302                     cls: 'masonry-brick-split-body',
40303                     cn: []
40304                 }
40305             ]
40306         };
40307         
40308         if(this.href.length){
40309             cfg.href = this.href;
40310         }
40311         
40312         if(this.title.length){
40313             cfg.cn[0].cn[0].cn.push({
40314                 tag: 'h4',
40315                 cls: 'masonry-brick-title',
40316                 html: this.title
40317             });
40318         }
40319         
40320         if(this.html.length){
40321             cfg.cn[1].cn.push({
40322                 tag: 'p',
40323                 cls: 'masonry-brick-text',
40324                 html: this.html
40325             });
40326         }
40327
40328         if(this.bgimage.length){
40329             cfg.cn[0].cn.push({
40330                 tag: 'img',
40331                 cls: 'masonry-brick-image-view',
40332                 src: this.bgimage
40333             });
40334         }
40335         
40336         if(this.videourl.length){
40337             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40338             // youtube support only?
40339             cfg.cn[0].cn.cn.push({
40340                 tag: 'iframe',
40341                 cls: 'masonry-brick-image-view',
40342                 src: vurl,
40343                 frameborder : 0,
40344                 allowfullscreen : true
40345             });
40346         }
40347         
40348         return cfg;
40349     },
40350     
40351     initEvents: function() 
40352     {
40353         switch (this.size) {
40354             case 'xs' :
40355                 this.x = 1;
40356                 this.y = 1;
40357                 break;
40358             case 'sm' :
40359                 this.x = 2;
40360                 this.y = 2;
40361                 break;
40362             case 'md' :
40363             case 'md-left' :
40364             case 'md-right' :
40365                 this.x = 3;
40366                 this.y = 3;
40367                 break;
40368             case 'tall' :
40369                 this.x = 2;
40370                 this.y = 3;
40371                 break;
40372             case 'wide' :
40373                 this.x = 3;
40374                 this.y = 2;
40375                 break;
40376             case 'wide-thin' :
40377                 this.x = 3;
40378                 this.y = 1;
40379                 break;
40380                         
40381             default :
40382                 break;
40383         }
40384         
40385         if(Roo.isTouch){
40386             this.el.on('touchstart', this.onTouchStart, this);
40387             this.el.on('touchmove', this.onTouchMove, this);
40388             this.el.on('touchend', this.onTouchEnd, this);
40389             this.el.on('contextmenu', this.onContextMenu, this);
40390         } else {
40391             this.el.on('mouseenter'  ,this.enter, this);
40392             this.el.on('mouseleave', this.leave, this);
40393             this.el.on('click', this.onClick, this);
40394         }
40395         
40396         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
40397             this.parent().bricks.push(this);   
40398         }
40399         
40400     },
40401     
40402     onClick: function(e, el)
40403     {
40404         var time = this.endTimer - this.startTimer;
40405         // Roo.log(e.preventDefault());
40406         if(Roo.isTouch){
40407             if(time > 1000){
40408                 e.preventDefault();
40409                 return;
40410             }
40411         }
40412         
40413         if(!this.preventDefault){
40414             return;
40415         }
40416         
40417         e.preventDefault();
40418         
40419         if (this.activeClass != '') {
40420             this.selectBrick();
40421         }
40422         
40423         this.fireEvent('click', this, e);
40424     },
40425     
40426     enter: function(e, el)
40427     {
40428         e.preventDefault();
40429         
40430         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40431             return;
40432         }
40433         
40434         if(this.bgimage.length && this.html.length){
40435             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40436         }
40437     },
40438     
40439     leave: function(e, el)
40440     {
40441         e.preventDefault();
40442         
40443         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
40444             return;
40445         }
40446         
40447         if(this.bgimage.length && this.html.length){
40448             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40449         }
40450     },
40451     
40452     onTouchStart: function(e, el)
40453     {
40454 //        e.preventDefault();
40455         
40456         this.touchmoved = false;
40457         
40458         if(!this.isFitContainer){
40459             return;
40460         }
40461         
40462         if(!this.bgimage.length || !this.html.length){
40463             return;
40464         }
40465         
40466         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40467         
40468         this.timer = new Date().getTime();
40469         
40470     },
40471     
40472     onTouchMove: function(e, el)
40473     {
40474         this.touchmoved = true;
40475     },
40476     
40477     onContextMenu : function(e,el)
40478     {
40479         e.preventDefault();
40480         e.stopPropagation();
40481         return false;
40482     },
40483     
40484     onTouchEnd: function(e, el)
40485     {
40486 //        e.preventDefault();
40487         
40488         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
40489         
40490             this.leave(e,el);
40491             
40492             return;
40493         }
40494         
40495         if(!this.bgimage.length || !this.html.length){
40496             
40497             if(this.href.length){
40498                 window.location.href = this.href;
40499             }
40500             
40501             return;
40502         }
40503         
40504         if(!this.isFitContainer){
40505             return;
40506         }
40507         
40508         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40509         
40510         window.location.href = this.href;
40511     },
40512     
40513     //selection on single brick only
40514     selectBrick : function() {
40515         
40516         if (!this.parentId) {
40517             return;
40518         }
40519         
40520         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
40521         var index = m.selectedBrick.indexOf(this.id);
40522         
40523         if ( index > -1) {
40524             m.selectedBrick.splice(index,1);
40525             this.el.removeClass(this.activeClass);
40526             return;
40527         }
40528         
40529         for(var i = 0; i < m.selectedBrick.length; i++) {
40530             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
40531             b.el.removeClass(b.activeClass);
40532         }
40533         
40534         m.selectedBrick = [];
40535         
40536         m.selectedBrick.push(this.id);
40537         this.el.addClass(this.activeClass);
40538         return;
40539     },
40540     
40541     isSelected : function(){
40542         return this.el.hasClass(this.activeClass);
40543         
40544     }
40545 });
40546
40547 Roo.apply(Roo.bootstrap.MasonryBrick, {
40548     
40549     //groups: {},
40550     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
40551      /**
40552     * register a Masonry Brick
40553     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40554     */
40555     
40556     register : function(brick)
40557     {
40558         //this.groups[brick.id] = brick;
40559         this.groups.add(brick.id, brick);
40560     },
40561     /**
40562     * fetch a  masonry brick based on the masonry brick ID
40563     * @param {string} the masonry brick to add
40564     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
40565     */
40566     
40567     get: function(brick_id) 
40568     {
40569         // if (typeof(this.groups[brick_id]) == 'undefined') {
40570         //     return false;
40571         // }
40572         // return this.groups[brick_id] ;
40573         
40574         if(this.groups.key(brick_id)) {
40575             return this.groups.key(brick_id);
40576         }
40577         
40578         return false;
40579     }
40580     
40581     
40582     
40583 });
40584
40585  /*
40586  * - LGPL
40587  *
40588  * element
40589  * 
40590  */
40591
40592 /**
40593  * @class Roo.bootstrap.Brick
40594  * @extends Roo.bootstrap.Component
40595  * Bootstrap Brick class
40596  * 
40597  * @constructor
40598  * Create a new Brick
40599  * @param {Object} config The config object
40600  */
40601
40602 Roo.bootstrap.Brick = function(config){
40603     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
40604     
40605     this.addEvents({
40606         // raw events
40607         /**
40608          * @event click
40609          * When a Brick is click
40610          * @param {Roo.bootstrap.Brick} this
40611          * @param {Roo.EventObject} e
40612          */
40613         "click" : true
40614     });
40615 };
40616
40617 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
40618     
40619     /**
40620      * @cfg {String} title
40621      */   
40622     title : '',
40623     /**
40624      * @cfg {String} html
40625      */   
40626     html : '',
40627     /**
40628      * @cfg {String} bgimage
40629      */   
40630     bgimage : '',
40631     /**
40632      * @cfg {String} cls
40633      */   
40634     cls : '',
40635     /**
40636      * @cfg {String} href
40637      */   
40638     href : '',
40639     /**
40640      * @cfg {String} video
40641      */   
40642     video : '',
40643     /**
40644      * @cfg {Boolean} square
40645      */   
40646     square : true,
40647     
40648     getAutoCreate : function()
40649     {
40650         var cls = 'roo-brick';
40651         
40652         if(this.href.length){
40653             cls += ' roo-brick-link';
40654         }
40655         
40656         if(this.bgimage.length){
40657             cls += ' roo-brick-image';
40658         }
40659         
40660         if(!this.html.length && !this.bgimage.length){
40661             cls += ' roo-brick-center-title';
40662         }
40663         
40664         if(!this.html.length && this.bgimage.length){
40665             cls += ' roo-brick-bottom-title';
40666         }
40667         
40668         if(this.cls){
40669             cls += ' ' + this.cls;
40670         }
40671         
40672         var cfg = {
40673             tag: (this.href.length) ? 'a' : 'div',
40674             cls: cls,
40675             cn: [
40676                 {
40677                     tag: 'div',
40678                     cls: 'roo-brick-paragraph',
40679                     cn: []
40680                 }
40681             ]
40682         };
40683         
40684         if(this.href.length){
40685             cfg.href = this.href;
40686         }
40687         
40688         var cn = cfg.cn[0].cn;
40689         
40690         if(this.title.length){
40691             cn.push({
40692                 tag: 'h4',
40693                 cls: 'roo-brick-title',
40694                 html: this.title
40695             });
40696         }
40697         
40698         if(this.html.length){
40699             cn.push({
40700                 tag: 'p',
40701                 cls: 'roo-brick-text',
40702                 html: this.html
40703             });
40704         } else {
40705             cn.cls += ' hide';
40706         }
40707         
40708         if(this.bgimage.length){
40709             cfg.cn.push({
40710                 tag: 'img',
40711                 cls: 'roo-brick-image-view',
40712                 src: this.bgimage
40713             });
40714         }
40715         
40716         return cfg;
40717     },
40718     
40719     initEvents: function() 
40720     {
40721         if(this.title.length || this.html.length){
40722             this.el.on('mouseenter'  ,this.enter, this);
40723             this.el.on('mouseleave', this.leave, this);
40724         }
40725         
40726         Roo.EventManager.onWindowResize(this.resize, this); 
40727         
40728         if(this.bgimage.length){
40729             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
40730             this.imageEl.on('load', this.onImageLoad, this);
40731             return;
40732         }
40733         
40734         this.resize();
40735     },
40736     
40737     onImageLoad : function()
40738     {
40739         this.resize();
40740     },
40741     
40742     resize : function()
40743     {
40744         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
40745         
40746         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
40747         
40748         if(this.bgimage.length){
40749             var image = this.el.select('.roo-brick-image-view', true).first();
40750             
40751             image.setWidth(paragraph.getWidth());
40752             
40753             if(this.square){
40754                 image.setHeight(paragraph.getWidth());
40755             }
40756             
40757             this.el.setHeight(image.getHeight());
40758             paragraph.setHeight(image.getHeight());
40759             
40760         }
40761         
40762     },
40763     
40764     enter: function(e, el)
40765     {
40766         e.preventDefault();
40767         
40768         if(this.bgimage.length){
40769             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
40770             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
40771         }
40772     },
40773     
40774     leave: function(e, el)
40775     {
40776         e.preventDefault();
40777         
40778         if(this.bgimage.length){
40779             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
40780             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
40781         }
40782     }
40783     
40784 });
40785
40786  
40787
40788  /*
40789  * - LGPL
40790  *
40791  * Number field 
40792  */
40793
40794 /**
40795  * @class Roo.bootstrap.form.NumberField
40796  * @extends Roo.bootstrap.form.Input
40797  * Bootstrap NumberField class
40798  * 
40799  * 
40800  * 
40801  * 
40802  * @constructor
40803  * Create a new NumberField
40804  * @param {Object} config The config object
40805  */
40806
40807 Roo.bootstrap.form.NumberField = function(config){
40808     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
40809 };
40810
40811 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
40812     
40813     /**
40814      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40815      */
40816     allowDecimals : true,
40817     /**
40818      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40819      */
40820     decimalSeparator : ".",
40821     /**
40822      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40823      */
40824     decimalPrecision : 2,
40825     /**
40826      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40827      */
40828     allowNegative : true,
40829     
40830     /**
40831      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40832      */
40833     allowZero: true,
40834     /**
40835      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40836      */
40837     minValue : Number.NEGATIVE_INFINITY,
40838     /**
40839      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40840      */
40841     maxValue : Number.MAX_VALUE,
40842     /**
40843      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40844      */
40845     minText : "The minimum value for this field is {0}",
40846     /**
40847      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40848      */
40849     maxText : "The maximum value for this field is {0}",
40850     /**
40851      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40852      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40853      */
40854     nanText : "{0} is not a valid number",
40855     /**
40856      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40857      */
40858     thousandsDelimiter : false,
40859     /**
40860      * @cfg {String} valueAlign alignment of value
40861      */
40862     valueAlign : "left",
40863
40864     getAutoCreate : function()
40865     {
40866         var hiddenInput = {
40867             tag: 'input',
40868             type: 'hidden',
40869             id: Roo.id(),
40870             cls: 'hidden-number-input'
40871         };
40872         
40873         if (this.name) {
40874             hiddenInput.name = this.name;
40875         }
40876         
40877         this.name = '';
40878         
40879         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
40880         
40881         this.name = hiddenInput.name;
40882         
40883         if(cfg.cn.length > 0) {
40884             cfg.cn.push(hiddenInput);
40885         }
40886         
40887         return cfg;
40888     },
40889
40890     // private
40891     initEvents : function()
40892     {   
40893         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
40894         
40895         var allowed = "0123456789";
40896         
40897         if(this.allowDecimals){
40898             allowed += this.decimalSeparator;
40899         }
40900         
40901         if(this.allowNegative){
40902             allowed += "-";
40903         }
40904         
40905         if(this.thousandsDelimiter) {
40906             allowed += ",";
40907         }
40908         
40909         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40910         
40911         var keyPress = function(e){
40912             
40913             var k = e.getKey();
40914             
40915             var c = e.getCharCode();
40916             
40917             if(
40918                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40919                     allowed.indexOf(String.fromCharCode(c)) === -1
40920             ){
40921                 e.stopEvent();
40922                 return;
40923             }
40924             
40925             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40926                 return;
40927             }
40928             
40929             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40930                 e.stopEvent();
40931             }
40932         };
40933         
40934         this.el.on("keypress", keyPress, this);
40935     },
40936     
40937     validateValue : function(value)
40938     {
40939         
40940         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
40941             return false;
40942         }
40943         
40944         var num = this.parseValue(value);
40945         
40946         if(isNaN(num)){
40947             this.markInvalid(String.format(this.nanText, value));
40948             return false;
40949         }
40950         
40951         if(num < this.minValue){
40952             this.markInvalid(String.format(this.minText, this.minValue));
40953             return false;
40954         }
40955         
40956         if(num > this.maxValue){
40957             this.markInvalid(String.format(this.maxText, this.maxValue));
40958             return false;
40959         }
40960         
40961         return true;
40962     },
40963
40964     getValue : function()
40965     {
40966         var v = this.hiddenEl().getValue();
40967         
40968         return this.fixPrecision(this.parseValue(v));
40969     },
40970
40971     parseValue : function(value)
40972     {
40973         if(this.thousandsDelimiter) {
40974             value += "";
40975             r = new RegExp(",", "g");
40976             value = value.replace(r, "");
40977         }
40978         
40979         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40980         return isNaN(value) ? '' : value;
40981     },
40982
40983     fixPrecision : function(value)
40984     {
40985         if(this.thousandsDelimiter) {
40986             value += "";
40987             r = new RegExp(",", "g");
40988             value = value.replace(r, "");
40989         }
40990         
40991         var nan = isNaN(value);
40992         
40993         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40994             return nan ? '' : value;
40995         }
40996         return parseFloat(value).toFixed(this.decimalPrecision);
40997     },
40998
40999     setValue : function(v)
41000     {
41001         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41002         
41003         this.value = v;
41004         
41005         if(this.rendered){
41006             
41007             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41008             
41009             this.inputEl().dom.value = (v == '') ? '' :
41010                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41011             
41012             if(!this.allowZero && v === '0') {
41013                 this.hiddenEl().dom.value = '';
41014                 this.inputEl().dom.value = '';
41015             }
41016             
41017             this.validate();
41018         }
41019     },
41020
41021     decimalPrecisionFcn : function(v)
41022     {
41023         return Math.floor(v);
41024     },
41025
41026     beforeBlur : function()
41027     {
41028         var v = this.parseValue(this.getRawValue());
41029         
41030         if(v || v === 0 || v === ''){
41031             this.setValue(v);
41032         }
41033     },
41034     
41035     hiddenEl : function()
41036     {
41037         return this.el.select('input.hidden-number-input',true).first();
41038     }
41039     
41040 });
41041
41042  
41043
41044 /*
41045 * Licence: LGPL
41046 */
41047
41048 /**
41049  * @class Roo.bootstrap.DocumentSlider
41050  * @extends Roo.bootstrap.Component
41051  * Bootstrap DocumentSlider class
41052  * 
41053  * @constructor
41054  * Create a new DocumentViewer
41055  * @param {Object} config The config object
41056  */
41057
41058 Roo.bootstrap.DocumentSlider = function(config){
41059     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41060     
41061     this.files = [];
41062     
41063     this.addEvents({
41064         /**
41065          * @event initial
41066          * Fire after initEvent
41067          * @param {Roo.bootstrap.DocumentSlider} this
41068          */
41069         "initial" : true,
41070         /**
41071          * @event update
41072          * Fire after update
41073          * @param {Roo.bootstrap.DocumentSlider} this
41074          */
41075         "update" : true,
41076         /**
41077          * @event click
41078          * Fire after click
41079          * @param {Roo.bootstrap.DocumentSlider} this
41080          */
41081         "click" : true
41082     });
41083 };
41084
41085 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
41086     
41087     files : false,
41088     
41089     indicator : 0,
41090     
41091     getAutoCreate : function()
41092     {
41093         var cfg = {
41094             tag : 'div',
41095             cls : 'roo-document-slider',
41096             cn : [
41097                 {
41098                     tag : 'div',
41099                     cls : 'roo-document-slider-header',
41100                     cn : [
41101                         {
41102                             tag : 'div',
41103                             cls : 'roo-document-slider-header-title'
41104                         }
41105                     ]
41106                 },
41107                 {
41108                     tag : 'div',
41109                     cls : 'roo-document-slider-body',
41110                     cn : [
41111                         {
41112                             tag : 'div',
41113                             cls : 'roo-document-slider-prev',
41114                             cn : [
41115                                 {
41116                                     tag : 'i',
41117                                     cls : 'fa fa-chevron-left'
41118                                 }
41119                             ]
41120                         },
41121                         {
41122                             tag : 'div',
41123                             cls : 'roo-document-slider-thumb',
41124                             cn : [
41125                                 {
41126                                     tag : 'img',
41127                                     cls : 'roo-document-slider-image'
41128                                 }
41129                             ]
41130                         },
41131                         {
41132                             tag : 'div',
41133                             cls : 'roo-document-slider-next',
41134                             cn : [
41135                                 {
41136                                     tag : 'i',
41137                                     cls : 'fa fa-chevron-right'
41138                                 }
41139                             ]
41140                         }
41141                     ]
41142                 }
41143             ]
41144         };
41145         
41146         return cfg;
41147     },
41148     
41149     initEvents : function()
41150     {
41151         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41152         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41153         
41154         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41155         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41156         
41157         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41158         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41159         
41160         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41161         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41162         
41163         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41164         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41165         
41166         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41167         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41168         
41169         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41170         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41171         
41172         this.thumbEl.on('click', this.onClick, this);
41173         
41174         this.prevIndicator.on('click', this.prev, this);
41175         
41176         this.nextIndicator.on('click', this.next, this);
41177         
41178     },
41179     
41180     initial : function()
41181     {
41182         if(this.files.length){
41183             this.indicator = 1;
41184             this.update()
41185         }
41186         
41187         this.fireEvent('initial', this);
41188     },
41189     
41190     update : function()
41191     {
41192         this.imageEl.attr('src', this.files[this.indicator - 1]);
41193         
41194         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
41195         
41196         this.prevIndicator.show();
41197         
41198         if(this.indicator == 1){
41199             this.prevIndicator.hide();
41200         }
41201         
41202         this.nextIndicator.show();
41203         
41204         if(this.indicator == this.files.length){
41205             this.nextIndicator.hide();
41206         }
41207         
41208         this.thumbEl.scrollTo('top');
41209         
41210         this.fireEvent('update', this);
41211     },
41212     
41213     onClick : function(e)
41214     {
41215         e.preventDefault();
41216         
41217         this.fireEvent('click', this);
41218     },
41219     
41220     prev : function(e)
41221     {
41222         e.preventDefault();
41223         
41224         this.indicator = Math.max(1, this.indicator - 1);
41225         
41226         this.update();
41227     },
41228     
41229     next : function(e)
41230     {
41231         e.preventDefault();
41232         
41233         this.indicator = Math.min(this.files.length, this.indicator + 1);
41234         
41235         this.update();
41236     }
41237 });
41238 /*
41239  * - LGPL
41240  *
41241  * RadioSet
41242  *
41243  *
41244  */
41245
41246 /**
41247  * @class Roo.bootstrap.form.RadioSet
41248  * @extends Roo.bootstrap.form.Input
41249  * @children Roo.bootstrap.form.Radio
41250  * Bootstrap RadioSet class
41251  * @cfg {String} indicatorpos (left|right) default left
41252  * @cfg {Boolean} inline (true|false) inline the element (default true)
41253  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
41254  * @constructor
41255  * Create a new RadioSet
41256  * @param {Object} config The config object
41257  */
41258
41259 Roo.bootstrap.form.RadioSet = function(config){
41260     
41261     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
41262     
41263     this.radioes = [];
41264     
41265     Roo.bootstrap.form.RadioSet.register(this);
41266     
41267     this.addEvents({
41268         /**
41269         * @event check
41270         * Fires when the element is checked or unchecked.
41271         * @param {Roo.bootstrap.form.RadioSet} this This radio
41272         * @param {Roo.bootstrap.form.Radio} item The checked item
41273         */
41274        check : true,
41275        /**
41276         * @event click
41277         * Fires when the element is click.
41278         * @param {Roo.bootstrap.form.RadioSet} this This radio set
41279         * @param {Roo.bootstrap.form.Radio} item The checked item
41280         * @param {Roo.EventObject} e The event object
41281         */
41282        click : true
41283     });
41284     
41285 };
41286
41287 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
41288
41289     radioes : false,
41290     
41291     inline : true,
41292     
41293     weight : '',
41294     
41295     indicatorpos : 'left',
41296     
41297     getAutoCreate : function()
41298     {
41299         var label = {
41300             tag : 'label',
41301             cls : 'roo-radio-set-label',
41302             cn : [
41303                 {
41304                     tag : 'span',
41305                     html : this.fieldLabel
41306                 }
41307             ]
41308         };
41309         if (Roo.bootstrap.version == 3) {
41310             
41311             
41312             if(this.indicatorpos == 'left'){
41313                 label.cn.unshift({
41314                     tag : 'i',
41315                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
41316                     tooltip : 'This field is required'
41317                 });
41318             } else {
41319                 label.cn.push({
41320                     tag : 'i',
41321                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
41322                     tooltip : 'This field is required'
41323                 });
41324             }
41325         }
41326         var items = {
41327             tag : 'div',
41328             cls : 'roo-radio-set-items'
41329         };
41330         
41331         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
41332         
41333         if (align === 'left' && this.fieldLabel.length) {
41334             
41335             items = {
41336                 cls : "roo-radio-set-right", 
41337                 cn: [
41338                     items
41339                 ]
41340             };
41341             
41342             if(this.labelWidth > 12){
41343                 label.style = "width: " + this.labelWidth + 'px';
41344             }
41345             
41346             if(this.labelWidth < 13 && this.labelmd == 0){
41347                 this.labelmd = this.labelWidth;
41348             }
41349             
41350             if(this.labellg > 0){
41351                 label.cls += ' col-lg-' + this.labellg;
41352                 items.cls += ' col-lg-' + (12 - this.labellg);
41353             }
41354             
41355             if(this.labelmd > 0){
41356                 label.cls += ' col-md-' + this.labelmd;
41357                 items.cls += ' col-md-' + (12 - this.labelmd);
41358             }
41359             
41360             if(this.labelsm > 0){
41361                 label.cls += ' col-sm-' + this.labelsm;
41362                 items.cls += ' col-sm-' + (12 - this.labelsm);
41363             }
41364             
41365             if(this.labelxs > 0){
41366                 label.cls += ' col-xs-' + this.labelxs;
41367                 items.cls += ' col-xs-' + (12 - this.labelxs);
41368             }
41369         }
41370         
41371         var cfg = {
41372             tag : 'div',
41373             cls : 'roo-radio-set',
41374             cn : [
41375                 {
41376                     tag : 'input',
41377                     cls : 'roo-radio-set-input',
41378                     type : 'hidden',
41379                     name : this.name,
41380                     value : this.value ? this.value :  ''
41381                 },
41382                 label,
41383                 items
41384             ]
41385         };
41386         
41387         if(this.weight.length){
41388             cfg.cls += ' roo-radio-' + this.weight;
41389         }
41390         
41391         if(this.inline) {
41392             cfg.cls += ' roo-radio-set-inline';
41393         }
41394         
41395         var settings=this;
41396         ['xs','sm','md','lg'].map(function(size){
41397             if (settings[size]) {
41398                 cfg.cls += ' col-' + size + '-' + settings[size];
41399             }
41400         });
41401         
41402         return cfg;
41403         
41404     },
41405
41406     initEvents : function()
41407     {
41408         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
41409         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
41410         
41411         if(!this.fieldLabel.length){
41412             this.labelEl.hide();
41413         }
41414         
41415         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
41416         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
41417         
41418         this.indicator = this.indicatorEl();
41419         
41420         if(this.indicator){
41421             this.indicator.addClass('invisible');
41422         }
41423         
41424         this.originalValue = this.getValue();
41425         
41426     },
41427     
41428     inputEl: function ()
41429     {
41430         return this.el.select('.roo-radio-set-input', true).first();
41431     },
41432     
41433     getChildContainer : function()
41434     {
41435         return this.itemsEl;
41436     },
41437     
41438     register : function(item)
41439     {
41440         this.radioes.push(item);
41441         
41442     },
41443     
41444     validate : function()
41445     {   
41446         if(this.getVisibilityEl().hasClass('hidden')){
41447             return true;
41448         }
41449         
41450         var valid = false;
41451         
41452         Roo.each(this.radioes, function(i){
41453             if(!i.checked){
41454                 return;
41455             }
41456             
41457             valid = true;
41458             return false;
41459         });
41460         
41461         if(this.allowBlank) {
41462             return true;
41463         }
41464         
41465         if(this.disabled || valid){
41466             this.markValid();
41467             return true;
41468         }
41469         
41470         this.markInvalid();
41471         return false;
41472         
41473     },
41474     
41475     markValid : function()
41476     {
41477         if(this.labelEl.isVisible(true) && this.indicatorEl()){
41478             this.indicatorEl().removeClass('visible');
41479             this.indicatorEl().addClass('invisible');
41480         }
41481         
41482         
41483         if (Roo.bootstrap.version == 3) {
41484             this.el.removeClass([this.invalidClass, this.validClass]);
41485             this.el.addClass(this.validClass);
41486         } else {
41487             this.el.removeClass(['is-invalid','is-valid']);
41488             this.el.addClass(['is-valid']);
41489         }
41490         this.fireEvent('valid', this);
41491     },
41492     
41493     markInvalid : function(msg)
41494     {
41495         if(this.allowBlank || this.disabled){
41496             return;
41497         }
41498         
41499         if(this.labelEl.isVisible(true) && this.indicatorEl()){
41500             this.indicatorEl().removeClass('invisible');
41501             this.indicatorEl().addClass('visible');
41502         }
41503         if (Roo.bootstrap.version == 3) {
41504             this.el.removeClass([this.invalidClass, this.validClass]);
41505             this.el.addClass(this.invalidClass);
41506         } else {
41507             this.el.removeClass(['is-invalid','is-valid']);
41508             this.el.addClass(['is-invalid']);
41509         }
41510         
41511         this.fireEvent('invalid', this, msg);
41512         
41513     },
41514     
41515     setValue : function(v, suppressEvent)
41516     {   
41517         if(this.value === v){
41518             return;
41519         }
41520         
41521         this.value = v;
41522         
41523         if(this.rendered){
41524             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41525         }
41526         
41527         Roo.each(this.radioes, function(i){
41528             i.checked = false;
41529             i.el.removeClass('checked');
41530         });
41531         
41532         Roo.each(this.radioes, function(i){
41533             
41534             if(i.value === v || i.value.toString() === v.toString()){
41535                 i.checked = true;
41536                 i.el.addClass('checked');
41537                 
41538                 if(suppressEvent !== true){
41539                     this.fireEvent('check', this, i);
41540                 }
41541                 
41542                 return false;
41543             }
41544             
41545         }, this);
41546         
41547         this.validate();
41548     },
41549     
41550     clearInvalid : function(){
41551         
41552         if(!this.el || this.preventMark){
41553             return;
41554         }
41555         
41556         this.el.removeClass([this.invalidClass]);
41557         
41558         this.fireEvent('valid', this);
41559     }
41560     
41561 });
41562
41563 Roo.apply(Roo.bootstrap.form.RadioSet, {
41564     
41565     groups: {},
41566     
41567     register : function(set)
41568     {
41569         this.groups[set.name] = set;
41570     },
41571     
41572     get: function(name) 
41573     {
41574         if (typeof(this.groups[name]) == 'undefined') {
41575             return false;
41576         }
41577         
41578         return this.groups[name] ;
41579     }
41580     
41581 });
41582 /*
41583  * Based on:
41584  * Ext JS Library 1.1.1
41585  * Copyright(c) 2006-2007, Ext JS, LLC.
41586  *
41587  * Originally Released Under LGPL - original licence link has changed is not relivant.
41588  *
41589  * Fork - LGPL
41590  * <script type="text/javascript">
41591  */
41592
41593
41594 /**
41595  * @class Roo.bootstrap.SplitBar
41596  * @extends Roo.util.Observable
41597  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
41598  * <br><br>
41599  * Usage:
41600  * <pre><code>
41601 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
41602                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
41603 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
41604 split.minSize = 100;
41605 split.maxSize = 600;
41606 split.animate = true;
41607 split.on('moved', splitterMoved);
41608 </code></pre>
41609  * @constructor
41610  * Create a new SplitBar
41611  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
41612  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
41613  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
41614  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
41615                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
41616                         position of the SplitBar).
41617  */
41618 Roo.bootstrap.SplitBar = function(cfg){
41619     
41620     /** @private */
41621     
41622     //{
41623     //  dragElement : elm
41624     //  resizingElement: el,
41625         // optional..
41626     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
41627     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
41628         // existingProxy ???
41629     //}
41630     
41631     this.el = Roo.get(cfg.dragElement, true);
41632     this.el.dom.unselectable = "on";
41633     /** @private */
41634     this.resizingEl = Roo.get(cfg.resizingElement, true);
41635
41636     /**
41637      * @private
41638      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
41639      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
41640      * @type Number
41641      */
41642     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
41643     
41644     /**
41645      * The minimum size of the resizing element. (Defaults to 0)
41646      * @type Number
41647      */
41648     this.minSize = 0;
41649     
41650     /**
41651      * The maximum size of the resizing element. (Defaults to 2000)
41652      * @type Number
41653      */
41654     this.maxSize = 2000;
41655     
41656     /**
41657      * Whether to animate the transition to the new size
41658      * @type Boolean
41659      */
41660     this.animate = false;
41661     
41662     /**
41663      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
41664      * @type Boolean
41665      */
41666     this.useShim = false;
41667     
41668     /** @private */
41669     this.shim = null;
41670     
41671     if(!cfg.existingProxy){
41672         /** @private */
41673         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
41674     }else{
41675         this.proxy = Roo.get(cfg.existingProxy).dom;
41676     }
41677     /** @private */
41678     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
41679     
41680     /** @private */
41681     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
41682     
41683     /** @private */
41684     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
41685     
41686     /** @private */
41687     this.dragSpecs = {};
41688     
41689     /**
41690      * @private The adapter to use to positon and resize elements
41691      */
41692     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41693     this.adapter.init(this);
41694     
41695     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41696         /** @private */
41697         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
41698         this.el.addClass("roo-splitbar-h");
41699     }else{
41700         /** @private */
41701         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
41702         this.el.addClass("roo-splitbar-v");
41703     }
41704     
41705     this.addEvents({
41706         /**
41707          * @event resize
41708          * Fires when the splitter is moved (alias for {@link #event-moved})
41709          * @param {Roo.bootstrap.SplitBar} this
41710          * @param {Number} newSize the new width or height
41711          */
41712         "resize" : true,
41713         /**
41714          * @event moved
41715          * Fires when the splitter is moved
41716          * @param {Roo.bootstrap.SplitBar} this
41717          * @param {Number} newSize the new width or height
41718          */
41719         "moved" : true,
41720         /**
41721          * @event beforeresize
41722          * Fires before the splitter is dragged
41723          * @param {Roo.bootstrap.SplitBar} this
41724          */
41725         "beforeresize" : true,
41726
41727         "beforeapply" : true
41728     });
41729
41730     Roo.util.Observable.call(this);
41731 };
41732
41733 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
41734     onStartProxyDrag : function(x, y){
41735         this.fireEvent("beforeresize", this);
41736         if(!this.overlay){
41737             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
41738             o.unselectable();
41739             o.enableDisplayMode("block");
41740             // all splitbars share the same overlay
41741             Roo.bootstrap.SplitBar.prototype.overlay = o;
41742         }
41743         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
41744         this.overlay.show();
41745         Roo.get(this.proxy).setDisplayed("block");
41746         var size = this.adapter.getElementSize(this);
41747         this.activeMinSize = this.getMinimumSize();;
41748         this.activeMaxSize = this.getMaximumSize();;
41749         var c1 = size - this.activeMinSize;
41750         var c2 = Math.max(this.activeMaxSize - size, 0);
41751         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41752             this.dd.resetConstraints();
41753             this.dd.setXConstraint(
41754                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
41755                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
41756             );
41757             this.dd.setYConstraint(0, 0);
41758         }else{
41759             this.dd.resetConstraints();
41760             this.dd.setXConstraint(0, 0);
41761             this.dd.setYConstraint(
41762                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
41763                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
41764             );
41765          }
41766         this.dragSpecs.startSize = size;
41767         this.dragSpecs.startPoint = [x, y];
41768         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
41769     },
41770     
41771     /** 
41772      * @private Called after the drag operation by the DDProxy
41773      */
41774     onEndProxyDrag : function(e){
41775         Roo.get(this.proxy).setDisplayed(false);
41776         var endPoint = Roo.lib.Event.getXY(e);
41777         if(this.overlay){
41778             this.overlay.hide();
41779         }
41780         var newSize;
41781         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41782             newSize = this.dragSpecs.startSize + 
41783                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
41784                     endPoint[0] - this.dragSpecs.startPoint[0] :
41785                     this.dragSpecs.startPoint[0] - endPoint[0]
41786                 );
41787         }else{
41788             newSize = this.dragSpecs.startSize + 
41789                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
41790                     endPoint[1] - this.dragSpecs.startPoint[1] :
41791                     this.dragSpecs.startPoint[1] - endPoint[1]
41792                 );
41793         }
41794         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
41795         if(newSize != this.dragSpecs.startSize){
41796             if(this.fireEvent('beforeapply', this, newSize) !== false){
41797                 this.adapter.setElementSize(this, newSize);
41798                 this.fireEvent("moved", this, newSize);
41799                 this.fireEvent("resize", this, newSize);
41800             }
41801         }
41802     },
41803     
41804     /**
41805      * Get the adapter this SplitBar uses
41806      * @return The adapter object
41807      */
41808     getAdapter : function(){
41809         return this.adapter;
41810     },
41811     
41812     /**
41813      * Set the adapter this SplitBar uses
41814      * @param {Object} adapter A SplitBar adapter object
41815      */
41816     setAdapter : function(adapter){
41817         this.adapter = adapter;
41818         this.adapter.init(this);
41819     },
41820     
41821     /**
41822      * Gets the minimum size for the resizing element
41823      * @return {Number} The minimum size
41824      */
41825     getMinimumSize : function(){
41826         return this.minSize;
41827     },
41828     
41829     /**
41830      * Sets the minimum size for the resizing element
41831      * @param {Number} minSize The minimum size
41832      */
41833     setMinimumSize : function(minSize){
41834         this.minSize = minSize;
41835     },
41836     
41837     /**
41838      * Gets the maximum size for the resizing element
41839      * @return {Number} The maximum size
41840      */
41841     getMaximumSize : function(){
41842         return this.maxSize;
41843     },
41844     
41845     /**
41846      * Sets the maximum size for the resizing element
41847      * @param {Number} maxSize The maximum size
41848      */
41849     setMaximumSize : function(maxSize){
41850         this.maxSize = maxSize;
41851     },
41852     
41853     /**
41854      * Sets the initialize size for the resizing element
41855      * @param {Number} size The initial size
41856      */
41857     setCurrentSize : function(size){
41858         var oldAnimate = this.animate;
41859         this.animate = false;
41860         this.adapter.setElementSize(this, size);
41861         this.animate = oldAnimate;
41862     },
41863     
41864     /**
41865      * Destroy this splitbar. 
41866      * @param {Boolean} removeEl True to remove the element
41867      */
41868     destroy : function(removeEl){
41869         if(this.shim){
41870             this.shim.remove();
41871         }
41872         this.dd.unreg();
41873         this.proxy.parentNode.removeChild(this.proxy);
41874         if(removeEl){
41875             this.el.remove();
41876         }
41877     }
41878 });
41879
41880 /**
41881  * @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.
41882  */
41883 Roo.bootstrap.SplitBar.createProxy = function(dir){
41884     var proxy = new Roo.Element(document.createElement("div"));
41885     proxy.unselectable();
41886     var cls = 'roo-splitbar-proxy';
41887     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
41888     document.body.appendChild(proxy.dom);
41889     return proxy.dom;
41890 };
41891
41892 /** 
41893  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
41894  * Default Adapter. It assumes the splitter and resizing element are not positioned
41895  * elements and only gets/sets the width of the element. Generally used for table based layouts.
41896  */
41897 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
41898 };
41899
41900 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
41901     // do nothing for now
41902     init : function(s){
41903     
41904     },
41905     /**
41906      * Called before drag operations to get the current size of the resizing element. 
41907      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41908      */
41909      getElementSize : function(s){
41910         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41911             return s.resizingEl.getWidth();
41912         }else{
41913             return s.resizingEl.getHeight();
41914         }
41915     },
41916     
41917     /**
41918      * Called after drag operations to set the size of the resizing element.
41919      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41920      * @param {Number} newSize The new size to set
41921      * @param {Function} onComplete A function to be invoked when resizing is complete
41922      */
41923     setElementSize : function(s, newSize, onComplete){
41924         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41925             if(!s.animate){
41926                 s.resizingEl.setWidth(newSize);
41927                 if(onComplete){
41928                     onComplete(s, newSize);
41929                 }
41930             }else{
41931                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
41932             }
41933         }else{
41934             
41935             if(!s.animate){
41936                 s.resizingEl.setHeight(newSize);
41937                 if(onComplete){
41938                     onComplete(s, newSize);
41939                 }
41940             }else{
41941                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
41942             }
41943         }
41944     }
41945 };
41946
41947 /** 
41948  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
41949  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
41950  * Adapter that  moves the splitter element to align with the resized sizing element. 
41951  * Used with an absolute positioned SplitBar.
41952  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
41953  * document.body, make sure you assign an id to the body element.
41954  */
41955 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
41956     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41957     this.container = Roo.get(container);
41958 };
41959
41960 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
41961     init : function(s){
41962         this.basic.init(s);
41963     },
41964     
41965     getElementSize : function(s){
41966         return this.basic.getElementSize(s);
41967     },
41968     
41969     setElementSize : function(s, newSize, onComplete){
41970         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
41971     },
41972     
41973     moveSplitter : function(s){
41974         var yes = Roo.bootstrap.SplitBar;
41975         switch(s.placement){
41976             case yes.LEFT:
41977                 s.el.setX(s.resizingEl.getRight());
41978                 break;
41979             case yes.RIGHT:
41980                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
41981                 break;
41982             case yes.TOP:
41983                 s.el.setY(s.resizingEl.getBottom());
41984                 break;
41985             case yes.BOTTOM:
41986                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
41987                 break;
41988         }
41989     }
41990 };
41991
41992 /**
41993  * Orientation constant - Create a vertical SplitBar
41994  * @static
41995  * @type Number
41996  */
41997 Roo.bootstrap.SplitBar.VERTICAL = 1;
41998
41999 /**
42000  * Orientation constant - Create a horizontal SplitBar
42001  * @static
42002  * @type Number
42003  */
42004 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42005
42006 /**
42007  * Placement constant - The resizing element is to the left of the splitter element
42008  * @static
42009  * @type Number
42010  */
42011 Roo.bootstrap.SplitBar.LEFT = 1;
42012
42013 /**
42014  * Placement constant - The resizing element is to the right of the splitter element
42015  * @static
42016  * @type Number
42017  */
42018 Roo.bootstrap.SplitBar.RIGHT = 2;
42019
42020 /**
42021  * Placement constant - The resizing element is positioned above the splitter element
42022  * @static
42023  * @type Number
42024  */
42025 Roo.bootstrap.SplitBar.TOP = 3;
42026
42027 /**
42028  * Placement constant - The resizing element is positioned under splitter element
42029  * @static
42030  * @type Number
42031  */
42032 Roo.bootstrap.SplitBar.BOTTOM = 4;
42033 /*
42034  * Based on:
42035  * Ext JS Library 1.1.1
42036  * Copyright(c) 2006-2007, Ext JS, LLC.
42037  *
42038  * Originally Released Under LGPL - original licence link has changed is not relivant.
42039  *
42040  * Fork - LGPL
42041  * <script type="text/javascript">
42042  */
42043
42044 /**
42045  * @class Roo.bootstrap.layout.Manager
42046  * @extends Roo.bootstrap.Component
42047  * @abstract
42048  * Base class for layout managers.
42049  */
42050 Roo.bootstrap.layout.Manager = function(config)
42051 {
42052     this.monitorWindowResize = true; // do this before we apply configuration.
42053     
42054     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42055
42056
42057
42058
42059
42060     /** false to disable window resize monitoring @type Boolean */
42061     
42062     this.regions = {};
42063     this.addEvents({
42064         /**
42065          * @event layout
42066          * Fires when a layout is performed.
42067          * @param {Roo.LayoutManager} this
42068          */
42069         "layout" : true,
42070         /**
42071          * @event regionresized
42072          * Fires when the user resizes a region.
42073          * @param {Roo.LayoutRegion} region The resized region
42074          * @param {Number} newSize The new size (width for east/west, height for north/south)
42075          */
42076         "regionresized" : true,
42077         /**
42078          * @event regioncollapsed
42079          * Fires when a region is collapsed.
42080          * @param {Roo.LayoutRegion} region The collapsed region
42081          */
42082         "regioncollapsed" : true,
42083         /**
42084          * @event regionexpanded
42085          * Fires when a region is expanded.
42086          * @param {Roo.LayoutRegion} region The expanded region
42087          */
42088         "regionexpanded" : true
42089     });
42090     this.updating = false;
42091
42092     if (config.el) {
42093         this.el = Roo.get(config.el);
42094         this.initEvents();
42095     }
42096
42097 };
42098
42099 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42100
42101
42102     regions : null,
42103
42104     monitorWindowResize : true,
42105
42106
42107     updating : false,
42108
42109
42110     onRender : function(ct, position)
42111     {
42112         if(!this.el){
42113             this.el = Roo.get(ct);
42114             this.initEvents();
42115         }
42116         //this.fireEvent('render',this);
42117     },
42118
42119
42120     initEvents: function()
42121     {
42122
42123
42124         // ie scrollbar fix
42125         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42126             document.body.scroll = "no";
42127         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42128             this.el.position('relative');
42129         }
42130         this.id = this.el.id;
42131         this.el.addClass("roo-layout-container");
42132         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42133         if(this.el.dom != document.body ) {
42134             this.el.on('resize', this.layout,this);
42135             this.el.on('show', this.layout,this);
42136         }
42137
42138     },
42139
42140     /**
42141      * Returns true if this layout is currently being updated
42142      * @return {Boolean}
42143      */
42144     isUpdating : function(){
42145         return this.updating;
42146     },
42147
42148     /**
42149      * Suspend the LayoutManager from doing auto-layouts while
42150      * making multiple add or remove calls
42151      */
42152     beginUpdate : function(){
42153         this.updating = true;
42154     },
42155
42156     /**
42157      * Restore auto-layouts and optionally disable the manager from performing a layout
42158      * @param {Boolean} noLayout true to disable a layout update
42159      */
42160     endUpdate : function(noLayout){
42161         this.updating = false;
42162         if(!noLayout){
42163             this.layout();
42164         }
42165     },
42166
42167     layout: function(){
42168         // abstract...
42169     },
42170
42171     onRegionResized : function(region, newSize){
42172         this.fireEvent("regionresized", region, newSize);
42173         this.layout();
42174     },
42175
42176     onRegionCollapsed : function(region){
42177         this.fireEvent("regioncollapsed", region);
42178     },
42179
42180     onRegionExpanded : function(region){
42181         this.fireEvent("regionexpanded", region);
42182     },
42183
42184     /**
42185      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42186      * performs box-model adjustments.
42187      * @return {Object} The size as an object {width: (the width), height: (the height)}
42188      */
42189     getViewSize : function()
42190     {
42191         var size;
42192         if(this.el.dom != document.body){
42193             size = this.el.getSize();
42194         }else{
42195             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42196         }
42197         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42198         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42199         return size;
42200     },
42201
42202     /**
42203      * Returns the Element this layout is bound to.
42204      * @return {Roo.Element}
42205      */
42206     getEl : function(){
42207         return this.el;
42208     },
42209
42210     /**
42211      * Returns the specified region.
42212      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42213      * @return {Roo.LayoutRegion}
42214      */
42215     getRegion : function(target){
42216         return this.regions[target.toLowerCase()];
42217     },
42218
42219     onWindowResize : function(){
42220         if(this.monitorWindowResize){
42221             this.layout();
42222         }
42223     }
42224 });
42225 /*
42226  * Based on:
42227  * Ext JS Library 1.1.1
42228  * Copyright(c) 2006-2007, Ext JS, LLC.
42229  *
42230  * Originally Released Under LGPL - original licence link has changed is not relivant.
42231  *
42232  * Fork - LGPL
42233  * <script type="text/javascript">
42234  */
42235 /**
42236  * @class Roo.bootstrap.layout.Border
42237  * @extends Roo.bootstrap.layout.Manager
42238  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
42239  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
42240  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42241  * please see: examples/bootstrap/nested.html<br><br>
42242  
42243 <b>The container the layout is rendered into can be either the body element or any other element.
42244 If it is not the body element, the container needs to either be an absolute positioned element,
42245 or you will need to add "position:relative" to the css of the container.  You will also need to specify
42246 the container size if it is not the body element.</b>
42247
42248 * @constructor
42249 * Create a new Border
42250 * @param {Object} config Configuration options
42251  */
42252 Roo.bootstrap.layout.Border = function(config){
42253     config = config || {};
42254     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
42255     
42256     
42257     
42258     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42259         if(config[region]){
42260             config[region].region = region;
42261             this.addRegion(config[region]);
42262         }
42263     },this);
42264     
42265 };
42266
42267 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
42268
42269 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
42270     
42271         /**
42272          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
42273          */
42274         /**
42275          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
42276          */
42277         /**
42278          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
42279          */
42280         /**
42281          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
42282          */
42283         /**
42284          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
42285          */
42286         
42287         
42288         
42289         
42290     parent : false, // this might point to a 'nest' or a ???
42291     
42292     /**
42293      * Creates and adds a new region if it doesn't already exist.
42294      * @param {String} target The target region key (north, south, east, west or center).
42295      * @param {Object} config The regions config object
42296      * @return {BorderLayoutRegion} The new region
42297      */
42298     addRegion : function(config)
42299     {
42300         if(!this.regions[config.region]){
42301             var r = this.factory(config);
42302             this.bindRegion(r);
42303         }
42304         return this.regions[config.region];
42305     },
42306
42307     // private (kinda)
42308     bindRegion : function(r){
42309         this.regions[r.config.region] = r;
42310         
42311         r.on("visibilitychange",    this.layout, this);
42312         r.on("paneladded",          this.layout, this);
42313         r.on("panelremoved",        this.layout, this);
42314         r.on("invalidated",         this.layout, this);
42315         r.on("resized",             this.onRegionResized, this);
42316         r.on("collapsed",           this.onRegionCollapsed, this);
42317         r.on("expanded",            this.onRegionExpanded, this);
42318     },
42319
42320     /**
42321      * Performs a layout update.
42322      */
42323     layout : function()
42324     {
42325         if(this.updating) {
42326             return;
42327         }
42328         
42329         // render all the rebions if they have not been done alreayd?
42330         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42331             if(this.regions[region] && !this.regions[region].bodyEl){
42332                 this.regions[region].onRender(this.el)
42333             }
42334         },this);
42335         
42336         var size = this.getViewSize();
42337         var w = size.width;
42338         var h = size.height;
42339         var centerW = w;
42340         var centerH = h;
42341         var centerY = 0;
42342         var centerX = 0;
42343         //var x = 0, y = 0;
42344
42345         var rs = this.regions;
42346         var north = rs["north"];
42347         var south = rs["south"]; 
42348         var west = rs["west"];
42349         var east = rs["east"];
42350         var center = rs["center"];
42351         //if(this.hideOnLayout){ // not supported anymore
42352             //c.el.setStyle("display", "none");
42353         //}
42354         if(north && north.isVisible()){
42355             var b = north.getBox();
42356             var m = north.getMargins();
42357             b.width = w - (m.left+m.right);
42358             b.x = m.left;
42359             b.y = m.top;
42360             centerY = b.height + b.y + m.bottom;
42361             centerH -= centerY;
42362             north.updateBox(this.safeBox(b));
42363         }
42364         if(south && south.isVisible()){
42365             var b = south.getBox();
42366             var m = south.getMargins();
42367             b.width = w - (m.left+m.right);
42368             b.x = m.left;
42369             var totalHeight = (b.height + m.top + m.bottom);
42370             b.y = h - totalHeight + m.top;
42371             centerH -= totalHeight;
42372             south.updateBox(this.safeBox(b));
42373         }
42374         if(west && west.isVisible()){
42375             var b = west.getBox();
42376             var m = west.getMargins();
42377             b.height = centerH - (m.top+m.bottom);
42378             b.x = m.left;
42379             b.y = centerY + m.top;
42380             var totalWidth = (b.width + m.left + m.right);
42381             centerX += totalWidth;
42382             centerW -= totalWidth;
42383             west.updateBox(this.safeBox(b));
42384         }
42385         if(east && east.isVisible()){
42386             var b = east.getBox();
42387             var m = east.getMargins();
42388             b.height = centerH - (m.top+m.bottom);
42389             var totalWidth = (b.width + m.left + m.right);
42390             b.x = w - totalWidth + m.left;
42391             b.y = centerY + m.top;
42392             centerW -= totalWidth;
42393             east.updateBox(this.safeBox(b));
42394         }
42395         if(center){
42396             var m = center.getMargins();
42397             var centerBox = {
42398                 x: centerX + m.left,
42399                 y: centerY + m.top,
42400                 width: centerW - (m.left+m.right),
42401                 height: centerH - (m.top+m.bottom)
42402             };
42403             //if(this.hideOnLayout){
42404                 //center.el.setStyle("display", "block");
42405             //}
42406             center.updateBox(this.safeBox(centerBox));
42407         }
42408         this.el.repaint();
42409         this.fireEvent("layout", this);
42410     },
42411
42412     // private
42413     safeBox : function(box){
42414         box.width = Math.max(0, box.width);
42415         box.height = Math.max(0, box.height);
42416         return box;
42417     },
42418
42419     /**
42420      * Adds a ContentPanel (or subclass) to this layout.
42421      * @param {String} target The target region key (north, south, east, west or center).
42422      * @param {Roo.ContentPanel} panel The panel to add
42423      * @return {Roo.ContentPanel} The added panel
42424      */
42425     add : function(target, panel){
42426          
42427         target = target.toLowerCase();
42428         return this.regions[target].add(panel);
42429     },
42430
42431     /**
42432      * Remove a ContentPanel (or subclass) to this layout.
42433      * @param {String} target The target region key (north, south, east, west or center).
42434      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42435      * @return {Roo.ContentPanel} The removed panel
42436      */
42437     remove : function(target, panel){
42438         target = target.toLowerCase();
42439         return this.regions[target].remove(panel);
42440     },
42441
42442     /**
42443      * Searches all regions for a panel with the specified id
42444      * @param {String} panelId
42445      * @return {Roo.ContentPanel} The panel or null if it wasn't found
42446      */
42447     findPanel : function(panelId){
42448         var rs = this.regions;
42449         for(var target in rs){
42450             if(typeof rs[target] != "function"){
42451                 var p = rs[target].getPanel(panelId);
42452                 if(p){
42453                     return p;
42454                 }
42455             }
42456         }
42457         return null;
42458     },
42459
42460     /**
42461      * Searches all regions for a panel with the specified id and activates (shows) it.
42462      * @param {String/ContentPanel} panelId The panels id or the panel itself
42463      * @return {Roo.ContentPanel} The shown panel or null
42464      */
42465     showPanel : function(panelId) {
42466       var rs = this.regions;
42467       for(var target in rs){
42468          var r = rs[target];
42469          if(typeof r != "function"){
42470             if(r.hasPanel(panelId)){
42471                return r.showPanel(panelId);
42472             }
42473          }
42474       }
42475       return null;
42476    },
42477
42478    /**
42479      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42480      * @param {Roo.state.Provider} provider (optional) An alternate state provider
42481      */
42482    /*
42483     restoreState : function(provider){
42484         if(!provider){
42485             provider = Roo.state.Manager;
42486         }
42487         var sm = new Roo.LayoutStateManager();
42488         sm.init(this, provider);
42489     },
42490 */
42491  
42492  
42493     /**
42494      * Adds a xtype elements to the layout.
42495      * <pre><code>
42496
42497 layout.addxtype({
42498        xtype : 'ContentPanel',
42499        region: 'west',
42500        items: [ .... ]
42501    }
42502 );
42503
42504 layout.addxtype({
42505         xtype : 'NestedLayoutPanel',
42506         region: 'west',
42507         layout: {
42508            center: { },
42509            west: { }   
42510         },
42511         items : [ ... list of content panels or nested layout panels.. ]
42512    }
42513 );
42514 </code></pre>
42515      * @param {Object} cfg Xtype definition of item to add.
42516      */
42517     addxtype : function(cfg)
42518     {
42519         // basically accepts a pannel...
42520         // can accept a layout region..!?!?
42521         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
42522         
42523         
42524         // theory?  children can only be panels??
42525         
42526         //if (!cfg.xtype.match(/Panel$/)) {
42527         //    return false;
42528         //}
42529         var ret = false;
42530         
42531         if (typeof(cfg.region) == 'undefined') {
42532             Roo.log("Failed to add Panel, region was not set");
42533             Roo.log(cfg);
42534             return false;
42535         }
42536         var region = cfg.region;
42537         delete cfg.region;
42538         
42539           
42540         var xitems = [];
42541         if (cfg.items) {
42542             xitems = cfg.items;
42543             delete cfg.items;
42544         }
42545         var nb = false;
42546         
42547         if ( region == 'center') {
42548             Roo.log("Center: " + cfg.title);
42549         }
42550         
42551         
42552         switch(cfg.xtype) 
42553         {
42554             case 'Content':  // ContentPanel (el, cfg)
42555             case 'Scroll':  // ContentPanel (el, cfg)
42556             case 'View': 
42557                 cfg.autoCreate = cfg.autoCreate || true;
42558                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42559                 //} else {
42560                 //    var el = this.el.createChild();
42561                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
42562                 //}
42563                 
42564                 this.add(region, ret);
42565                 break;
42566             
42567             /*
42568             case 'TreePanel': // our new panel!
42569                 cfg.el = this.el.createChild();
42570                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42571                 this.add(region, ret);
42572                 break;
42573             */
42574             
42575             case 'Nest': 
42576                 // create a new Layout (which is  a Border Layout...
42577                 
42578                 var clayout = cfg.layout;
42579                 clayout.el  = this.el.createChild();
42580                 clayout.items   = clayout.items  || [];
42581                 
42582                 delete cfg.layout;
42583                 
42584                 // replace this exitems with the clayout ones..
42585                 xitems = clayout.items;
42586                  
42587                 // force background off if it's in center...
42588                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
42589                     cfg.background = false;
42590                 }
42591                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
42592                 
42593                 
42594                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42595                 //console.log('adding nested layout panel '  + cfg.toSource());
42596                 this.add(region, ret);
42597                 nb = {}; /// find first...
42598                 break;
42599             
42600             case 'Grid':
42601                 
42602                 // needs grid and region
42603                 
42604                 //var el = this.getRegion(region).el.createChild();
42605                 /*
42606                  *var el = this.el.createChild();
42607                 // create the grid first...
42608                 cfg.grid.container = el;
42609                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
42610                 */
42611                 
42612                 if (region == 'center' && this.active ) {
42613                     cfg.background = false;
42614                 }
42615                 
42616                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42617                 
42618                 this.add(region, ret);
42619                 /*
42620                 if (cfg.background) {
42621                     // render grid on panel activation (if panel background)
42622                     ret.on('activate', function(gp) {
42623                         if (!gp.grid.rendered) {
42624                     //        gp.grid.render(el);
42625                         }
42626                     });
42627                 } else {
42628                   //  cfg.grid.render(el);
42629                 }
42630                 */
42631                 break;
42632            
42633            
42634             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
42635                 // it was the old xcomponent building that caused this before.
42636                 // espeically if border is the top element in the tree.
42637                 ret = this;
42638                 break; 
42639                 
42640                     
42641                 
42642                 
42643                 
42644             default:
42645                 /*
42646                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
42647                     
42648                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42649                     this.add(region, ret);
42650                 } else {
42651                 */
42652                     Roo.log(cfg);
42653                     throw "Can not add '" + cfg.xtype + "' to Border";
42654                     return null;
42655              
42656                                 
42657              
42658         }
42659         this.beginUpdate();
42660         // add children..
42661         var region = '';
42662         var abn = {};
42663         Roo.each(xitems, function(i)  {
42664             region = nb && i.region ? i.region : false;
42665             
42666             var add = ret.addxtype(i);
42667            
42668             if (region) {
42669                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
42670                 if (!i.background) {
42671                     abn[region] = nb[region] ;
42672                 }
42673             }
42674             
42675         });
42676         this.endUpdate();
42677
42678         // make the last non-background panel active..
42679         //if (nb) { Roo.log(abn); }
42680         if (nb) {
42681             
42682             for(var r in abn) {
42683                 region = this.getRegion(r);
42684                 if (region) {
42685                     // tried using nb[r], but it does not work..
42686                      
42687                     region.showPanel(abn[r]);
42688                    
42689                 }
42690             }
42691         }
42692         return ret;
42693         
42694     },
42695     
42696     
42697 // private
42698     factory : function(cfg)
42699     {
42700         
42701         var validRegions = Roo.bootstrap.layout.Border.regions;
42702
42703         var target = cfg.region;
42704         cfg.mgr = this;
42705         
42706         var r = Roo.bootstrap.layout;
42707         Roo.log(target);
42708         switch(target){
42709             case "north":
42710                 return new r.North(cfg);
42711             case "south":
42712                 return new r.South(cfg);
42713             case "east":
42714                 return new r.East(cfg);
42715             case "west":
42716                 return new r.West(cfg);
42717             case "center":
42718                 return new r.Center(cfg);
42719         }
42720         throw 'Layout region "'+target+'" not supported.';
42721     }
42722     
42723     
42724 });
42725  /*
42726  * Based on:
42727  * Ext JS Library 1.1.1
42728  * Copyright(c) 2006-2007, Ext JS, LLC.
42729  *
42730  * Originally Released Under LGPL - original licence link has changed is not relivant.
42731  *
42732  * Fork - LGPL
42733  * <script type="text/javascript">
42734  */
42735  
42736 /**
42737  * @class Roo.bootstrap.layout.Basic
42738  * @extends Roo.util.Observable
42739  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
42740  * and does not have a titlebar, tabs or any other features. All it does is size and position 
42741  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
42742  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
42743  * @cfg {string}   region  the region that it inhabits..
42744  * @cfg {bool}   skipConfig skip config?
42745  * 
42746
42747  */
42748 Roo.bootstrap.layout.Basic = function(config){
42749     
42750     this.mgr = config.mgr;
42751     
42752     this.position = config.region;
42753     
42754     var skipConfig = config.skipConfig;
42755     
42756     this.events = {
42757         /**
42758          * @scope Roo.BasicLayoutRegion
42759          */
42760         
42761         /**
42762          * @event beforeremove
42763          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
42764          * @param {Roo.LayoutRegion} this
42765          * @param {Roo.ContentPanel} panel The panel
42766          * @param {Object} e The cancel event object
42767          */
42768         "beforeremove" : true,
42769         /**
42770          * @event invalidated
42771          * Fires when the layout for this region is changed.
42772          * @param {Roo.LayoutRegion} this
42773          */
42774         "invalidated" : true,
42775         /**
42776          * @event visibilitychange
42777          * Fires when this region is shown or hidden 
42778          * @param {Roo.LayoutRegion} this
42779          * @param {Boolean} visibility true or false
42780          */
42781         "visibilitychange" : true,
42782         /**
42783          * @event paneladded
42784          * Fires when a panel is added. 
42785          * @param {Roo.LayoutRegion} this
42786          * @param {Roo.ContentPanel} panel The panel
42787          */
42788         "paneladded" : true,
42789         /**
42790          * @event panelremoved
42791          * Fires when a panel is removed. 
42792          * @param {Roo.LayoutRegion} this
42793          * @param {Roo.ContentPanel} panel The panel
42794          */
42795         "panelremoved" : true,
42796         /**
42797          * @event beforecollapse
42798          * Fires when this region before collapse.
42799          * @param {Roo.LayoutRegion} this
42800          */
42801         "beforecollapse" : true,
42802         /**
42803          * @event collapsed
42804          * Fires when this region is collapsed.
42805          * @param {Roo.LayoutRegion} this
42806          */
42807         "collapsed" : true,
42808         /**
42809          * @event expanded
42810          * Fires when this region is expanded.
42811          * @param {Roo.LayoutRegion} this
42812          */
42813         "expanded" : true,
42814         /**
42815          * @event slideshow
42816          * Fires when this region is slid into view.
42817          * @param {Roo.LayoutRegion} this
42818          */
42819         "slideshow" : true,
42820         /**
42821          * @event slidehide
42822          * Fires when this region slides out of view. 
42823          * @param {Roo.LayoutRegion} this
42824          */
42825         "slidehide" : true,
42826         /**
42827          * @event panelactivated
42828          * Fires when a panel is activated. 
42829          * @param {Roo.LayoutRegion} this
42830          * @param {Roo.ContentPanel} panel The activated panel
42831          */
42832         "panelactivated" : true,
42833         /**
42834          * @event resized
42835          * Fires when the user resizes this region. 
42836          * @param {Roo.LayoutRegion} this
42837          * @param {Number} newSize The new size (width for east/west, height for north/south)
42838          */
42839         "resized" : true
42840     };
42841     /** A collection of panels in this region. @type Roo.util.MixedCollection */
42842     this.panels = new Roo.util.MixedCollection();
42843     this.panels.getKey = this.getPanelId.createDelegate(this);
42844     this.box = null;
42845     this.activePanel = null;
42846     // ensure listeners are added...
42847     
42848     if (config.listeners || config.events) {
42849         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
42850             listeners : config.listeners || {},
42851             events : config.events || {}
42852         });
42853     }
42854     
42855     if(skipConfig !== true){
42856         this.applyConfig(config);
42857     }
42858 };
42859
42860 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
42861 {
42862     getPanelId : function(p){
42863         return p.getId();
42864     },
42865     
42866     applyConfig : function(config){
42867         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
42868         this.config = config;
42869         
42870     },
42871     
42872     /**
42873      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
42874      * the width, for horizontal (north, south) the height.
42875      * @param {Number} newSize The new width or height
42876      */
42877     resizeTo : function(newSize){
42878         var el = this.el ? this.el :
42879                  (this.activePanel ? this.activePanel.getEl() : null);
42880         if(el){
42881             switch(this.position){
42882                 case "east":
42883                 case "west":
42884                     el.setWidth(newSize);
42885                     this.fireEvent("resized", this, newSize);
42886                 break;
42887                 case "north":
42888                 case "south":
42889                     el.setHeight(newSize);
42890                     this.fireEvent("resized", this, newSize);
42891                 break;                
42892             }
42893         }
42894     },
42895     
42896     getBox : function(){
42897         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
42898     },
42899     
42900     getMargins : function(){
42901         return this.margins;
42902     },
42903     
42904     updateBox : function(box){
42905         this.box = box;
42906         var el = this.activePanel.getEl();
42907         el.dom.style.left = box.x + "px";
42908         el.dom.style.top = box.y + "px";
42909         this.activePanel.setSize(box.width, box.height);
42910     },
42911     
42912     /**
42913      * Returns the container element for this region.
42914      * @return {Roo.Element}
42915      */
42916     getEl : function(){
42917         return this.activePanel;
42918     },
42919     
42920     /**
42921      * Returns true if this region is currently visible.
42922      * @return {Boolean}
42923      */
42924     isVisible : function(){
42925         return this.activePanel ? true : false;
42926     },
42927     
42928     setActivePanel : function(panel){
42929         panel = this.getPanel(panel);
42930         if(this.activePanel && this.activePanel != panel){
42931             this.activePanel.setActiveState(false);
42932             this.activePanel.getEl().setLeftTop(-10000,-10000);
42933         }
42934         this.activePanel = panel;
42935         panel.setActiveState(true);
42936         if(this.box){
42937             panel.setSize(this.box.width, this.box.height);
42938         }
42939         this.fireEvent("panelactivated", this, panel);
42940         this.fireEvent("invalidated");
42941     },
42942     
42943     /**
42944      * Show the specified panel.
42945      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
42946      * @return {Roo.ContentPanel} The shown panel or null
42947      */
42948     showPanel : function(panel){
42949         panel = this.getPanel(panel);
42950         if(panel){
42951             this.setActivePanel(panel);
42952         }
42953         return panel;
42954     },
42955     
42956     /**
42957      * Get the active panel for this region.
42958      * @return {Roo.ContentPanel} The active panel or null
42959      */
42960     getActivePanel : function(){
42961         return this.activePanel;
42962     },
42963     
42964     /**
42965      * Add the passed ContentPanel(s)
42966      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42967      * @return {Roo.ContentPanel} The panel added (if only one was added)
42968      */
42969     add : function(panel){
42970         if(arguments.length > 1){
42971             for(var i = 0, len = arguments.length; i < len; i++) {
42972                 this.add(arguments[i]);
42973             }
42974             return null;
42975         }
42976         if(this.hasPanel(panel)){
42977             this.showPanel(panel);
42978             return panel;
42979         }
42980         var el = panel.getEl();
42981         if(el.dom.parentNode != this.mgr.el.dom){
42982             this.mgr.el.dom.appendChild(el.dom);
42983         }
42984         if(panel.setRegion){
42985             panel.setRegion(this);
42986         }
42987         this.panels.add(panel);
42988         el.setStyle("position", "absolute");
42989         if(!panel.background){
42990             this.setActivePanel(panel);
42991             if(this.config.initialSize && this.panels.getCount()==1){
42992                 this.resizeTo(this.config.initialSize);
42993             }
42994         }
42995         this.fireEvent("paneladded", this, panel);
42996         return panel;
42997     },
42998     
42999     /**
43000      * Returns true if the panel is in this region.
43001      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43002      * @return {Boolean}
43003      */
43004     hasPanel : function(panel){
43005         if(typeof panel == "object"){ // must be panel obj
43006             panel = panel.getId();
43007         }
43008         return this.getPanel(panel) ? true : false;
43009     },
43010     
43011     /**
43012      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43013      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43014      * @param {Boolean} preservePanel Overrides the config preservePanel option
43015      * @return {Roo.ContentPanel} The panel that was removed
43016      */
43017     remove : function(panel, preservePanel){
43018         panel = this.getPanel(panel);
43019         if(!panel){
43020             return null;
43021         }
43022         var e = {};
43023         this.fireEvent("beforeremove", this, panel, e);
43024         if(e.cancel === true){
43025             return null;
43026         }
43027         var panelId = panel.getId();
43028         this.panels.removeKey(panelId);
43029         return panel;
43030     },
43031     
43032     /**
43033      * Returns the panel specified or null if it's not in this region.
43034      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43035      * @return {Roo.ContentPanel}
43036      */
43037     getPanel : function(id){
43038         if(typeof id == "object"){ // must be panel obj
43039             return id;
43040         }
43041         return this.panels.get(id);
43042     },
43043     
43044     /**
43045      * Returns this regions position (north/south/east/west/center).
43046      * @return {String} 
43047      */
43048     getPosition: function(){
43049         return this.position;    
43050     }
43051 });/*
43052  * Based on:
43053  * Ext JS Library 1.1.1
43054  * Copyright(c) 2006-2007, Ext JS, LLC.
43055  *
43056  * Originally Released Under LGPL - original licence link has changed is not relivant.
43057  *
43058  * Fork - LGPL
43059  * <script type="text/javascript">
43060  */
43061  
43062 /**
43063  * @class Roo.bootstrap.layout.Region
43064  * @extends Roo.bootstrap.layout.Basic
43065  * This class represents a region in a layout manager.
43066  
43067  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43068  * @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})
43069  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
43070  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
43071  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
43072  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
43073  * @cfg {String}    title           The title for the region (overrides panel titles)
43074  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
43075  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43076  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
43077  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43078  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
43079  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43080  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
43081  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
43082  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
43083  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
43084
43085  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
43086  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
43087  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
43088  * @cfg {Number}    width           For East/West panels
43089  * @cfg {Number}    height          For North/South panels
43090  * @cfg {Boolean}   split           To show the splitter
43091  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
43092  * 
43093  * @cfg {string}   cls             Extra CSS classes to add to region
43094  * 
43095  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43096  * @cfg {string}   region  the region that it inhabits..
43097  *
43098
43099  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
43100  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
43101
43102  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
43103  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
43104  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
43105  */
43106 Roo.bootstrap.layout.Region = function(config)
43107 {
43108     this.applyConfig(config);
43109
43110     var mgr = config.mgr;
43111     var pos = config.region;
43112     config.skipConfig = true;
43113     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43114     
43115     if (mgr.el) {
43116         this.onRender(mgr.el);   
43117     }
43118      
43119     this.visible = true;
43120     this.collapsed = false;
43121     this.unrendered_panels = [];
43122 };
43123
43124 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43125
43126     position: '', // set by wrapper (eg. north/south etc..)
43127     unrendered_panels : null,  // unrendered panels.
43128     
43129     tabPosition : false,
43130     
43131     mgr: false, // points to 'Border'
43132     
43133     
43134     createBody : function(){
43135         /** This region's body element 
43136         * @type Roo.Element */
43137         this.bodyEl = this.el.createChild({
43138                 tag: "div",
43139                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43140         });
43141     },
43142
43143     onRender: function(ctr, pos)
43144     {
43145         var dh = Roo.DomHelper;
43146         /** This region's container element 
43147         * @type Roo.Element */
43148         this.el = dh.append(ctr.dom, {
43149                 tag: "div",
43150                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43151             }, true);
43152         /** This region's title element 
43153         * @type Roo.Element */
43154     
43155         this.titleEl = dh.append(this.el.dom,  {
43156                 tag: "div",
43157                 unselectable: "on",
43158                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43159                 children:[
43160                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43161                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43162                 ]
43163             }, true);
43164         
43165         this.titleEl.enableDisplayMode();
43166         /** This region's title text element 
43167         * @type HTMLElement */
43168         this.titleTextEl = this.titleEl.dom.firstChild;
43169         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43170         /*
43171         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43172         this.closeBtn.enableDisplayMode();
43173         this.closeBtn.on("click", this.closeClicked, this);
43174         this.closeBtn.hide();
43175     */
43176         this.createBody(this.config);
43177         if(this.config.hideWhenEmpty){
43178             this.hide();
43179             this.on("paneladded", this.validateVisibility, this);
43180             this.on("panelremoved", this.validateVisibility, this);
43181         }
43182         if(this.autoScroll){
43183             this.bodyEl.setStyle("overflow", "auto");
43184         }else{
43185             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
43186         }
43187         //if(c.titlebar !== false){
43188             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
43189                 this.titleEl.hide();
43190             }else{
43191                 this.titleEl.show();
43192                 if(this.config.title){
43193                     this.titleTextEl.innerHTML = this.config.title;
43194                 }
43195             }
43196         //}
43197         if(this.config.collapsed){
43198             this.collapse(true);
43199         }
43200         if(this.config.hidden){
43201             this.hide();
43202         }
43203         
43204         if (this.unrendered_panels && this.unrendered_panels.length) {
43205             for (var i =0;i< this.unrendered_panels.length; i++) {
43206                 this.add(this.unrendered_panels[i]);
43207             }
43208             this.unrendered_panels = null;
43209             
43210         }
43211         
43212     },
43213     
43214     applyConfig : function(c)
43215     {
43216         /*
43217          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
43218             var dh = Roo.DomHelper;
43219             if(c.titlebar !== false){
43220                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
43221                 this.collapseBtn.on("click", this.collapse, this);
43222                 this.collapseBtn.enableDisplayMode();
43223                 /*
43224                 if(c.showPin === true || this.showPin){
43225                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
43226                     this.stickBtn.enableDisplayMode();
43227                     this.stickBtn.on("click", this.expand, this);
43228                     this.stickBtn.hide();
43229                 }
43230                 
43231             }
43232             */
43233             /** This region's collapsed element
43234             * @type Roo.Element */
43235             /*
43236              *
43237             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43238                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43239             ]}, true);
43240             
43241             if(c.floatable !== false){
43242                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43243                this.collapsedEl.on("click", this.collapseClick, this);
43244             }
43245
43246             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43247                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43248                    id: "message", unselectable: "on", style:{"float":"left"}});
43249                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43250              }
43251             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43252             this.expandBtn.on("click", this.expand, this);
43253             
43254         }
43255         
43256         if(this.collapseBtn){
43257             this.collapseBtn.setVisible(c.collapsible == true);
43258         }
43259         
43260         this.cmargins = c.cmargins || this.cmargins ||
43261                          (this.position == "west" || this.position == "east" ?
43262                              {top: 0, left: 2, right:2, bottom: 0} :
43263                              {top: 2, left: 0, right:0, bottom: 2});
43264         */
43265         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43266         
43267         
43268         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
43269         
43270         this.autoScroll = c.autoScroll || false;
43271         
43272         
43273        
43274         
43275         this.duration = c.duration || .30;
43276         this.slideDuration = c.slideDuration || .45;
43277         this.config = c;
43278        
43279     },
43280     /**
43281      * Returns true if this region is currently visible.
43282      * @return {Boolean}
43283      */
43284     isVisible : function(){
43285         return this.visible;
43286     },
43287
43288     /**
43289      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43290      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
43291      */
43292     //setCollapsedTitle : function(title){
43293     //    title = title || "&#160;";
43294      //   if(this.collapsedTitleTextEl){
43295       //      this.collapsedTitleTextEl.innerHTML = title;
43296        // }
43297     //},
43298
43299     getBox : function(){
43300         var b;
43301       //  if(!this.collapsed){
43302             b = this.el.getBox(false, true);
43303        // }else{
43304           //  b = this.collapsedEl.getBox(false, true);
43305         //}
43306         return b;
43307     },
43308
43309     getMargins : function(){
43310         return this.margins;
43311         //return this.collapsed ? this.cmargins : this.margins;
43312     },
43313 /*
43314     highlight : function(){
43315         this.el.addClass("x-layout-panel-dragover");
43316     },
43317
43318     unhighlight : function(){
43319         this.el.removeClass("x-layout-panel-dragover");
43320     },
43321 */
43322     updateBox : function(box)
43323     {
43324         if (!this.bodyEl) {
43325             return; // not rendered yet..
43326         }
43327         
43328         this.box = box;
43329         if(!this.collapsed){
43330             this.el.dom.style.left = box.x + "px";
43331             this.el.dom.style.top = box.y + "px";
43332             this.updateBody(box.width, box.height);
43333         }else{
43334             this.collapsedEl.dom.style.left = box.x + "px";
43335             this.collapsedEl.dom.style.top = box.y + "px";
43336             this.collapsedEl.setSize(box.width, box.height);
43337         }
43338         if(this.tabs){
43339             this.tabs.autoSizeTabs();
43340         }
43341     },
43342
43343     updateBody : function(w, h)
43344     {
43345         if(w !== null){
43346             this.el.setWidth(w);
43347             w -= this.el.getBorderWidth("rl");
43348             if(this.config.adjustments){
43349                 w += this.config.adjustments[0];
43350             }
43351         }
43352         if(h !== null && h > 0){
43353             this.el.setHeight(h);
43354             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43355             h -= this.el.getBorderWidth("tb");
43356             if(this.config.adjustments){
43357                 h += this.config.adjustments[1];
43358             }
43359             this.bodyEl.setHeight(h);
43360             if(this.tabs){
43361                 h = this.tabs.syncHeight(h);
43362             }
43363         }
43364         if(this.panelSize){
43365             w = w !== null ? w : this.panelSize.width;
43366             h = h !== null ? h : this.panelSize.height;
43367         }
43368         if(this.activePanel){
43369             var el = this.activePanel.getEl();
43370             w = w !== null ? w : el.getWidth();
43371             h = h !== null ? h : el.getHeight();
43372             this.panelSize = {width: w, height: h};
43373             this.activePanel.setSize(w, h);
43374         }
43375         if(Roo.isIE && this.tabs){
43376             this.tabs.el.repaint();
43377         }
43378     },
43379
43380     /**
43381      * Returns the container element for this region.
43382      * @return {Roo.Element}
43383      */
43384     getEl : function(){
43385         return this.el;
43386     },
43387
43388     /**
43389      * Hides this region.
43390      */
43391     hide : function(){
43392         //if(!this.collapsed){
43393             this.el.dom.style.left = "-2000px";
43394             this.el.hide();
43395         //}else{
43396          //   this.collapsedEl.dom.style.left = "-2000px";
43397          //   this.collapsedEl.hide();
43398        // }
43399         this.visible = false;
43400         this.fireEvent("visibilitychange", this, false);
43401     },
43402
43403     /**
43404      * Shows this region if it was previously hidden.
43405      */
43406     show : function(){
43407         //if(!this.collapsed){
43408             this.el.show();
43409         //}else{
43410         //    this.collapsedEl.show();
43411        // }
43412         this.visible = true;
43413         this.fireEvent("visibilitychange", this, true);
43414     },
43415 /*
43416     closeClicked : function(){
43417         if(this.activePanel){
43418             this.remove(this.activePanel);
43419         }
43420     },
43421
43422     collapseClick : function(e){
43423         if(this.isSlid){
43424            e.stopPropagation();
43425            this.slideIn();
43426         }else{
43427            e.stopPropagation();
43428            this.slideOut();
43429         }
43430     },
43431 */
43432     /**
43433      * Collapses this region.
43434      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43435      */
43436     /*
43437     collapse : function(skipAnim, skipCheck = false){
43438         if(this.collapsed) {
43439             return;
43440         }
43441         
43442         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
43443             
43444             this.collapsed = true;
43445             if(this.split){
43446                 this.split.el.hide();
43447             }
43448             if(this.config.animate && skipAnim !== true){
43449                 this.fireEvent("invalidated", this);
43450                 this.animateCollapse();
43451             }else{
43452                 this.el.setLocation(-20000,-20000);
43453                 this.el.hide();
43454                 this.collapsedEl.show();
43455                 this.fireEvent("collapsed", this);
43456                 this.fireEvent("invalidated", this);
43457             }
43458         }
43459         
43460     },
43461 */
43462     animateCollapse : function(){
43463         // overridden
43464     },
43465
43466     /**
43467      * Expands this region if it was previously collapsed.
43468      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43469      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43470      */
43471     /*
43472     expand : function(e, skipAnim){
43473         if(e) {
43474             e.stopPropagation();
43475         }
43476         if(!this.collapsed || this.el.hasActiveFx()) {
43477             return;
43478         }
43479         if(this.isSlid){
43480             this.afterSlideIn();
43481             skipAnim = true;
43482         }
43483         this.collapsed = false;
43484         if(this.config.animate && skipAnim !== true){
43485             this.animateExpand();
43486         }else{
43487             this.el.show();
43488             if(this.split){
43489                 this.split.el.show();
43490             }
43491             this.collapsedEl.setLocation(-2000,-2000);
43492             this.collapsedEl.hide();
43493             this.fireEvent("invalidated", this);
43494             this.fireEvent("expanded", this);
43495         }
43496     },
43497 */
43498     animateExpand : function(){
43499         // overridden
43500     },
43501
43502     initTabs : function()
43503     {
43504         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
43505         
43506         var ts = new Roo.bootstrap.panel.Tabs({
43507             el: this.bodyEl.dom,
43508             region : this,
43509             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
43510             disableTooltips: this.config.disableTabTips,
43511             toolbar : this.config.toolbar
43512         });
43513         
43514         if(this.config.hideTabs){
43515             ts.stripWrap.setDisplayed(false);
43516         }
43517         this.tabs = ts;
43518         ts.resizeTabs = this.config.resizeTabs === true;
43519         ts.minTabWidth = this.config.minTabWidth || 40;
43520         ts.maxTabWidth = this.config.maxTabWidth || 250;
43521         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
43522         ts.monitorResize = false;
43523         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
43524         ts.bodyEl.addClass('roo-layout-tabs-body');
43525         this.panels.each(this.initPanelAsTab, this);
43526     },
43527
43528     initPanelAsTab : function(panel){
43529         var ti = this.tabs.addTab(
43530             panel.getEl().id,
43531             panel.getTitle(),
43532             null,
43533             this.config.closeOnTab && panel.isClosable(),
43534             panel.tpl
43535         );
43536         if(panel.tabTip !== undefined){
43537             ti.setTooltip(panel.tabTip);
43538         }
43539         ti.on("activate", function(){
43540               this.setActivePanel(panel);
43541         }, this);
43542         
43543         if(this.config.closeOnTab){
43544             ti.on("beforeclose", function(t, e){
43545                 e.cancel = true;
43546                 this.remove(panel);
43547             }, this);
43548         }
43549         
43550         panel.tabItem = ti;
43551         
43552         return ti;
43553     },
43554
43555     updatePanelTitle : function(panel, title)
43556     {
43557         if(this.activePanel == panel){
43558             this.updateTitle(title);
43559         }
43560         if(this.tabs){
43561             var ti = this.tabs.getTab(panel.getEl().id);
43562             ti.setText(title);
43563             if(panel.tabTip !== undefined){
43564                 ti.setTooltip(panel.tabTip);
43565             }
43566         }
43567     },
43568
43569     updateTitle : function(title){
43570         if(this.titleTextEl && !this.config.title){
43571             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
43572         }
43573     },
43574
43575     setActivePanel : function(panel)
43576     {
43577         panel = this.getPanel(panel);
43578         if(this.activePanel && this.activePanel != panel){
43579             if(this.activePanel.setActiveState(false) === false){
43580                 return;
43581             }
43582         }
43583         this.activePanel = panel;
43584         panel.setActiveState(true);
43585         if(this.panelSize){
43586             panel.setSize(this.panelSize.width, this.panelSize.height);
43587         }
43588         if(this.closeBtn){
43589             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
43590         }
43591         this.updateTitle(panel.getTitle());
43592         if(this.tabs){
43593             this.fireEvent("invalidated", this);
43594         }
43595         this.fireEvent("panelactivated", this, panel);
43596     },
43597
43598     /**
43599      * Shows the specified panel.
43600      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
43601      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
43602      */
43603     showPanel : function(panel)
43604     {
43605         panel = this.getPanel(panel);
43606         if(panel){
43607             if(this.tabs){
43608                 var tab = this.tabs.getTab(panel.getEl().id);
43609                 if(tab.isHidden()){
43610                     this.tabs.unhideTab(tab.id);
43611                 }
43612                 tab.activate();
43613             }else{
43614                 this.setActivePanel(panel);
43615             }
43616         }
43617         return panel;
43618     },
43619
43620     /**
43621      * Get the active panel for this region.
43622      * @return {Roo.ContentPanel} The active panel or null
43623      */
43624     getActivePanel : function(){
43625         return this.activePanel;
43626     },
43627
43628     validateVisibility : function(){
43629         if(this.panels.getCount() < 1){
43630             this.updateTitle("&#160;");
43631             this.closeBtn.hide();
43632             this.hide();
43633         }else{
43634             if(!this.isVisible()){
43635                 this.show();
43636             }
43637         }
43638     },
43639
43640     /**
43641      * Adds the passed ContentPanel(s) to this region.
43642      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43643      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
43644      */
43645     add : function(panel)
43646     {
43647         if(arguments.length > 1){
43648             for(var i = 0, len = arguments.length; i < len; i++) {
43649                 this.add(arguments[i]);
43650             }
43651             return null;
43652         }
43653         
43654         // if we have not been rendered yet, then we can not really do much of this..
43655         if (!this.bodyEl) {
43656             this.unrendered_panels.push(panel);
43657             return panel;
43658         }
43659         
43660         
43661         
43662         
43663         if(this.hasPanel(panel)){
43664             this.showPanel(panel);
43665             return panel;
43666         }
43667         panel.setRegion(this);
43668         this.panels.add(panel);
43669        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
43670             // sinle panel - no tab...?? would it not be better to render it with the tabs,
43671             // and hide them... ???
43672             this.bodyEl.dom.appendChild(panel.getEl().dom);
43673             if(panel.background !== true){
43674                 this.setActivePanel(panel);
43675             }
43676             this.fireEvent("paneladded", this, panel);
43677             return panel;
43678         }
43679         */
43680         if(!this.tabs){
43681             this.initTabs();
43682         }else{
43683             this.initPanelAsTab(panel);
43684         }
43685         
43686         
43687         if(panel.background !== true){
43688             this.tabs.activate(panel.getEl().id);
43689         }
43690         this.fireEvent("paneladded", this, panel);
43691         return panel;
43692     },
43693
43694     /**
43695      * Hides the tab for the specified panel.
43696      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43697      */
43698     hidePanel : function(panel){
43699         if(this.tabs && (panel = this.getPanel(panel))){
43700             this.tabs.hideTab(panel.getEl().id);
43701         }
43702     },
43703
43704     /**
43705      * Unhides the tab for a previously hidden panel.
43706      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43707      */
43708     unhidePanel : function(panel){
43709         if(this.tabs && (panel = this.getPanel(panel))){
43710             this.tabs.unhideTab(panel.getEl().id);
43711         }
43712     },
43713
43714     clearPanels : function(){
43715         while(this.panels.getCount() > 0){
43716              this.remove(this.panels.first());
43717         }
43718     },
43719
43720     /**
43721      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43722      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43723      * @param {Boolean} preservePanel Overrides the config preservePanel option
43724      * @return {Roo.ContentPanel} The panel that was removed
43725      */
43726     remove : function(panel, preservePanel)
43727     {
43728         panel = this.getPanel(panel);
43729         if(!panel){
43730             return null;
43731         }
43732         var e = {};
43733         this.fireEvent("beforeremove", this, panel, e);
43734         if(e.cancel === true){
43735             return null;
43736         }
43737         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
43738         var panelId = panel.getId();
43739         this.panels.removeKey(panelId);
43740         if(preservePanel){
43741             document.body.appendChild(panel.getEl().dom);
43742         }
43743         if(this.tabs){
43744             this.tabs.removeTab(panel.getEl().id);
43745         }else if (!preservePanel){
43746             this.bodyEl.dom.removeChild(panel.getEl().dom);
43747         }
43748         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
43749             var p = this.panels.first();
43750             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
43751             tempEl.appendChild(p.getEl().dom);
43752             this.bodyEl.update("");
43753             this.bodyEl.dom.appendChild(p.getEl().dom);
43754             tempEl = null;
43755             this.updateTitle(p.getTitle());
43756             this.tabs = null;
43757             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43758             this.setActivePanel(p);
43759         }
43760         panel.setRegion(null);
43761         if(this.activePanel == panel){
43762             this.activePanel = null;
43763         }
43764         if(this.config.autoDestroy !== false && preservePanel !== true){
43765             try{panel.destroy();}catch(e){}
43766         }
43767         this.fireEvent("panelremoved", this, panel);
43768         return panel;
43769     },
43770
43771     /**
43772      * Returns the TabPanel component used by this region
43773      * @return {Roo.TabPanel}
43774      */
43775     getTabs : function(){
43776         return this.tabs;
43777     },
43778
43779     createTool : function(parentEl, className){
43780         var btn = Roo.DomHelper.append(parentEl, {
43781             tag: "div",
43782             cls: "x-layout-tools-button",
43783             children: [ {
43784                 tag: "div",
43785                 cls: "roo-layout-tools-button-inner " + className,
43786                 html: "&#160;"
43787             }]
43788         }, true);
43789         btn.addClassOnOver("roo-layout-tools-button-over");
43790         return btn;
43791     }
43792 });/*
43793  * Based on:
43794  * Ext JS Library 1.1.1
43795  * Copyright(c) 2006-2007, Ext JS, LLC.
43796  *
43797  * Originally Released Under LGPL - original licence link has changed is not relivant.
43798  *
43799  * Fork - LGPL
43800  * <script type="text/javascript">
43801  */
43802  
43803
43804
43805 /**
43806  * @class Roo.SplitLayoutRegion
43807  * @extends Roo.LayoutRegion
43808  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
43809  */
43810 Roo.bootstrap.layout.Split = function(config){
43811     this.cursor = config.cursor;
43812     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
43813 };
43814
43815 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
43816 {
43817     splitTip : "Drag to resize.",
43818     collapsibleSplitTip : "Drag to resize. Double click to hide.",
43819     useSplitTips : false,
43820
43821     applyConfig : function(config){
43822         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
43823     },
43824     
43825     onRender : function(ctr,pos) {
43826         
43827         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
43828         if(!this.config.split){
43829             return;
43830         }
43831         if(!this.split){
43832             
43833             var splitEl = Roo.DomHelper.append(ctr.dom,  {
43834                             tag: "div",
43835                             id: this.el.id + "-split",
43836                             cls: "roo-layout-split roo-layout-split-"+this.position,
43837                             html: "&#160;"
43838             });
43839             /** The SplitBar for this region 
43840             * @type Roo.SplitBar */
43841             // does not exist yet...
43842             Roo.log([this.position, this.orientation]);
43843             
43844             this.split = new Roo.bootstrap.SplitBar({
43845                 dragElement : splitEl,
43846                 resizingElement: this.el,
43847                 orientation : this.orientation
43848             });
43849             
43850             this.split.on("moved", this.onSplitMove, this);
43851             this.split.useShim = this.config.useShim === true;
43852             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
43853             if(this.useSplitTips){
43854                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
43855             }
43856             //if(config.collapsible){
43857             //    this.split.el.on("dblclick", this.collapse,  this);
43858             //}
43859         }
43860         if(typeof this.config.minSize != "undefined"){
43861             this.split.minSize = this.config.minSize;
43862         }
43863         if(typeof this.config.maxSize != "undefined"){
43864             this.split.maxSize = this.config.maxSize;
43865         }
43866         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
43867             this.hideSplitter();
43868         }
43869         
43870     },
43871
43872     getHMaxSize : function(){
43873          var cmax = this.config.maxSize || 10000;
43874          var center = this.mgr.getRegion("center");
43875          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
43876     },
43877
43878     getVMaxSize : function(){
43879          var cmax = this.config.maxSize || 10000;
43880          var center = this.mgr.getRegion("center");
43881          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
43882     },
43883
43884     onSplitMove : function(split, newSize){
43885         this.fireEvent("resized", this, newSize);
43886     },
43887     
43888     /** 
43889      * Returns the {@link Roo.SplitBar} for this region.
43890      * @return {Roo.SplitBar}
43891      */
43892     getSplitBar : function(){
43893         return this.split;
43894     },
43895     
43896     hide : function(){
43897         this.hideSplitter();
43898         Roo.bootstrap.layout.Split.superclass.hide.call(this);
43899     },
43900
43901     hideSplitter : function(){
43902         if(this.split){
43903             this.split.el.setLocation(-2000,-2000);
43904             this.split.el.hide();
43905         }
43906     },
43907
43908     show : function(){
43909         if(this.split){
43910             this.split.el.show();
43911         }
43912         Roo.bootstrap.layout.Split.superclass.show.call(this);
43913     },
43914     
43915     beforeSlide: function(){
43916         if(Roo.isGecko){// firefox overflow auto bug workaround
43917             this.bodyEl.clip();
43918             if(this.tabs) {
43919                 this.tabs.bodyEl.clip();
43920             }
43921             if(this.activePanel){
43922                 this.activePanel.getEl().clip();
43923                 
43924                 if(this.activePanel.beforeSlide){
43925                     this.activePanel.beforeSlide();
43926                 }
43927             }
43928         }
43929     },
43930     
43931     afterSlide : function(){
43932         if(Roo.isGecko){// firefox overflow auto bug workaround
43933             this.bodyEl.unclip();
43934             if(this.tabs) {
43935                 this.tabs.bodyEl.unclip();
43936             }
43937             if(this.activePanel){
43938                 this.activePanel.getEl().unclip();
43939                 if(this.activePanel.afterSlide){
43940                     this.activePanel.afterSlide();
43941                 }
43942             }
43943         }
43944     },
43945
43946     initAutoHide : function(){
43947         if(this.autoHide !== false){
43948             if(!this.autoHideHd){
43949                 var st = new Roo.util.DelayedTask(this.slideIn, this);
43950                 this.autoHideHd = {
43951                     "mouseout": function(e){
43952                         if(!e.within(this.el, true)){
43953                             st.delay(500);
43954                         }
43955                     },
43956                     "mouseover" : function(e){
43957                         st.cancel();
43958                     },
43959                     scope : this
43960                 };
43961             }
43962             this.el.on(this.autoHideHd);
43963         }
43964     },
43965
43966     clearAutoHide : function(){
43967         if(this.autoHide !== false){
43968             this.el.un("mouseout", this.autoHideHd.mouseout);
43969             this.el.un("mouseover", this.autoHideHd.mouseover);
43970         }
43971     },
43972
43973     clearMonitor : function(){
43974         Roo.get(document).un("click", this.slideInIf, this);
43975     },
43976
43977     // these names are backwards but not changed for compat
43978     slideOut : function(){
43979         if(this.isSlid || this.el.hasActiveFx()){
43980             return;
43981         }
43982         this.isSlid = true;
43983         if(this.collapseBtn){
43984             this.collapseBtn.hide();
43985         }
43986         this.closeBtnState = this.closeBtn.getStyle('display');
43987         this.closeBtn.hide();
43988         if(this.stickBtn){
43989             this.stickBtn.show();
43990         }
43991         this.el.show();
43992         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
43993         this.beforeSlide();
43994         this.el.setStyle("z-index", 10001);
43995         this.el.slideIn(this.getSlideAnchor(), {
43996             callback: function(){
43997                 this.afterSlide();
43998                 this.initAutoHide();
43999                 Roo.get(document).on("click", this.slideInIf, this);
44000                 this.fireEvent("slideshow", this);
44001             },
44002             scope: this,
44003             block: true
44004         });
44005     },
44006
44007     afterSlideIn : function(){
44008         this.clearAutoHide();
44009         this.isSlid = false;
44010         this.clearMonitor();
44011         this.el.setStyle("z-index", "");
44012         if(this.collapseBtn){
44013             this.collapseBtn.show();
44014         }
44015         this.closeBtn.setStyle('display', this.closeBtnState);
44016         if(this.stickBtn){
44017             this.stickBtn.hide();
44018         }
44019         this.fireEvent("slidehide", this);
44020     },
44021
44022     slideIn : function(cb){
44023         if(!this.isSlid || this.el.hasActiveFx()){
44024             Roo.callback(cb);
44025             return;
44026         }
44027         this.isSlid = false;
44028         this.beforeSlide();
44029         this.el.slideOut(this.getSlideAnchor(), {
44030             callback: function(){
44031                 this.el.setLeftTop(-10000, -10000);
44032                 this.afterSlide();
44033                 this.afterSlideIn();
44034                 Roo.callback(cb);
44035             },
44036             scope: this,
44037             block: true
44038         });
44039     },
44040     
44041     slideInIf : function(e){
44042         if(!e.within(this.el)){
44043             this.slideIn();
44044         }
44045     },
44046
44047     animateCollapse : function(){
44048         this.beforeSlide();
44049         this.el.setStyle("z-index", 20000);
44050         var anchor = this.getSlideAnchor();
44051         this.el.slideOut(anchor, {
44052             callback : function(){
44053                 this.el.setStyle("z-index", "");
44054                 this.collapsedEl.slideIn(anchor, {duration:.3});
44055                 this.afterSlide();
44056                 this.el.setLocation(-10000,-10000);
44057                 this.el.hide();
44058                 this.fireEvent("collapsed", this);
44059             },
44060             scope: this,
44061             block: true
44062         });
44063     },
44064
44065     animateExpand : function(){
44066         this.beforeSlide();
44067         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44068         this.el.setStyle("z-index", 20000);
44069         this.collapsedEl.hide({
44070             duration:.1
44071         });
44072         this.el.slideIn(this.getSlideAnchor(), {
44073             callback : function(){
44074                 this.el.setStyle("z-index", "");
44075                 this.afterSlide();
44076                 if(this.split){
44077                     this.split.el.show();
44078                 }
44079                 this.fireEvent("invalidated", this);
44080                 this.fireEvent("expanded", this);
44081             },
44082             scope: this,
44083             block: true
44084         });
44085     },
44086
44087     anchors : {
44088         "west" : "left",
44089         "east" : "right",
44090         "north" : "top",
44091         "south" : "bottom"
44092     },
44093
44094     sanchors : {
44095         "west" : "l",
44096         "east" : "r",
44097         "north" : "t",
44098         "south" : "b"
44099     },
44100
44101     canchors : {
44102         "west" : "tl-tr",
44103         "east" : "tr-tl",
44104         "north" : "tl-bl",
44105         "south" : "bl-tl"
44106     },
44107
44108     getAnchor : function(){
44109         return this.anchors[this.position];
44110     },
44111
44112     getCollapseAnchor : function(){
44113         return this.canchors[this.position];
44114     },
44115
44116     getSlideAnchor : function(){
44117         return this.sanchors[this.position];
44118     },
44119
44120     getAlignAdj : function(){
44121         var cm = this.cmargins;
44122         switch(this.position){
44123             case "west":
44124                 return [0, 0];
44125             break;
44126             case "east":
44127                 return [0, 0];
44128             break;
44129             case "north":
44130                 return [0, 0];
44131             break;
44132             case "south":
44133                 return [0, 0];
44134             break;
44135         }
44136     },
44137
44138     getExpandAdj : function(){
44139         var c = this.collapsedEl, cm = this.cmargins;
44140         switch(this.position){
44141             case "west":
44142                 return [-(cm.right+c.getWidth()+cm.left), 0];
44143             break;
44144             case "east":
44145                 return [cm.right+c.getWidth()+cm.left, 0];
44146             break;
44147             case "north":
44148                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44149             break;
44150             case "south":
44151                 return [0, cm.top+cm.bottom+c.getHeight()];
44152             break;
44153         }
44154     }
44155 });/*
44156  * Based on:
44157  * Ext JS Library 1.1.1
44158  * Copyright(c) 2006-2007, Ext JS, LLC.
44159  *
44160  * Originally Released Under LGPL - original licence link has changed is not relivant.
44161  *
44162  * Fork - LGPL
44163  * <script type="text/javascript">
44164  */
44165 /*
44166  * These classes are private internal classes
44167  */
44168 Roo.bootstrap.layout.Center = function(config){
44169     config.region = "center";
44170     Roo.bootstrap.layout.Region.call(this, config);
44171     this.visible = true;
44172     this.minWidth = config.minWidth || 20;
44173     this.minHeight = config.minHeight || 20;
44174 };
44175
44176 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
44177     hide : function(){
44178         // center panel can't be hidden
44179     },
44180     
44181     show : function(){
44182         // center panel can't be hidden
44183     },
44184     
44185     getMinWidth: function(){
44186         return this.minWidth;
44187     },
44188     
44189     getMinHeight: function(){
44190         return this.minHeight;
44191     }
44192 });
44193
44194
44195
44196
44197  
44198
44199
44200
44201
44202
44203
44204 Roo.bootstrap.layout.North = function(config)
44205 {
44206     config.region = 'north';
44207     config.cursor = 'n-resize';
44208     
44209     Roo.bootstrap.layout.Split.call(this, config);
44210     
44211     
44212     if(this.split){
44213         this.split.placement = Roo.bootstrap.SplitBar.TOP;
44214         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44215         this.split.el.addClass("roo-layout-split-v");
44216     }
44217     //var size = config.initialSize || config.height;
44218     //if(this.el && typeof size != "undefined"){
44219     //    this.el.setHeight(size);
44220     //}
44221 };
44222 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
44223 {
44224     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44225      
44226      
44227     onRender : function(ctr, pos)
44228     {
44229         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44230         var size = this.config.initialSize || this.config.height;
44231         if(this.el && typeof size != "undefined"){
44232             this.el.setHeight(size);
44233         }
44234     
44235     },
44236     
44237     getBox : function(){
44238         if(this.collapsed){
44239             return this.collapsedEl.getBox();
44240         }
44241         var box = this.el.getBox();
44242         if(this.split){
44243             box.height += this.split.el.getHeight();
44244         }
44245         return box;
44246     },
44247     
44248     updateBox : function(box){
44249         if(this.split && !this.collapsed){
44250             box.height -= this.split.el.getHeight();
44251             this.split.el.setLeft(box.x);
44252             this.split.el.setTop(box.y+box.height);
44253             this.split.el.setWidth(box.width);
44254         }
44255         if(this.collapsed){
44256             this.updateBody(box.width, null);
44257         }
44258         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44259     }
44260 });
44261
44262
44263
44264
44265
44266 Roo.bootstrap.layout.South = function(config){
44267     config.region = 'south';
44268     config.cursor = 's-resize';
44269     Roo.bootstrap.layout.Split.call(this, config);
44270     if(this.split){
44271         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
44272         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44273         this.split.el.addClass("roo-layout-split-v");
44274     }
44275     
44276 };
44277
44278 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
44279     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44280     
44281     onRender : function(ctr, pos)
44282     {
44283         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44284         var size = this.config.initialSize || this.config.height;
44285         if(this.el && typeof size != "undefined"){
44286             this.el.setHeight(size);
44287         }
44288     
44289     },
44290     
44291     getBox : function(){
44292         if(this.collapsed){
44293             return this.collapsedEl.getBox();
44294         }
44295         var box = this.el.getBox();
44296         if(this.split){
44297             var sh = this.split.el.getHeight();
44298             box.height += sh;
44299             box.y -= sh;
44300         }
44301         return box;
44302     },
44303     
44304     updateBox : function(box){
44305         if(this.split && !this.collapsed){
44306             var sh = this.split.el.getHeight();
44307             box.height -= sh;
44308             box.y += sh;
44309             this.split.el.setLeft(box.x);
44310             this.split.el.setTop(box.y-sh);
44311             this.split.el.setWidth(box.width);
44312         }
44313         if(this.collapsed){
44314             this.updateBody(box.width, null);
44315         }
44316         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44317     }
44318 });
44319
44320 Roo.bootstrap.layout.East = function(config){
44321     config.region = "east";
44322     config.cursor = "e-resize";
44323     Roo.bootstrap.layout.Split.call(this, config);
44324     if(this.split){
44325         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
44326         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44327         this.split.el.addClass("roo-layout-split-h");
44328     }
44329     
44330 };
44331 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
44332     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44333     
44334     onRender : function(ctr, pos)
44335     {
44336         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44337         var size = this.config.initialSize || this.config.width;
44338         if(this.el && typeof size != "undefined"){
44339             this.el.setWidth(size);
44340         }
44341     
44342     },
44343     
44344     getBox : function(){
44345         if(this.collapsed){
44346             return this.collapsedEl.getBox();
44347         }
44348         var box = this.el.getBox();
44349         if(this.split){
44350             var sw = this.split.el.getWidth();
44351             box.width += sw;
44352             box.x -= sw;
44353         }
44354         return box;
44355     },
44356
44357     updateBox : function(box){
44358         if(this.split && !this.collapsed){
44359             var sw = this.split.el.getWidth();
44360             box.width -= sw;
44361             this.split.el.setLeft(box.x);
44362             this.split.el.setTop(box.y);
44363             this.split.el.setHeight(box.height);
44364             box.x += sw;
44365         }
44366         if(this.collapsed){
44367             this.updateBody(null, box.height);
44368         }
44369         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44370     }
44371 });
44372
44373 Roo.bootstrap.layout.West = function(config){
44374     config.region = "west";
44375     config.cursor = "w-resize";
44376     
44377     Roo.bootstrap.layout.Split.call(this, config);
44378     if(this.split){
44379         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
44380         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44381         this.split.el.addClass("roo-layout-split-h");
44382     }
44383     
44384 };
44385 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
44386     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44387     
44388     onRender: function(ctr, pos)
44389     {
44390         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
44391         var size = this.config.initialSize || this.config.width;
44392         if(typeof size != "undefined"){
44393             this.el.setWidth(size);
44394         }
44395     },
44396     
44397     getBox : function(){
44398         if(this.collapsed){
44399             return this.collapsedEl.getBox();
44400         }
44401         var box = this.el.getBox();
44402         if (box.width == 0) {
44403             box.width = this.config.width; // kludge?
44404         }
44405         if(this.split){
44406             box.width += this.split.el.getWidth();
44407         }
44408         return box;
44409     },
44410     
44411     updateBox : function(box){
44412         if(this.split && !this.collapsed){
44413             var sw = this.split.el.getWidth();
44414             box.width -= sw;
44415             this.split.el.setLeft(box.x+box.width);
44416             this.split.el.setTop(box.y);
44417             this.split.el.setHeight(box.height);
44418         }
44419         if(this.collapsed){
44420             this.updateBody(null, box.height);
44421         }
44422         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44423     }
44424 });/*
44425  * Based on:
44426  * Ext JS Library 1.1.1
44427  * Copyright(c) 2006-2007, Ext JS, LLC.
44428  *
44429  * Originally Released Under LGPL - original licence link has changed is not relivant.
44430  *
44431  * Fork - LGPL
44432  * <script type="text/javascript">
44433  */
44434 /**
44435  * @class Roo.bootstrap.paenl.Content
44436  * @extends Roo.util.Observable
44437  * @children Roo.bootstrap.Component
44438  * @parent builder Roo.bootstrap.layout.Border
44439  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
44440  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
44441  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
44442  * @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
44443  * @cfg {Boolean}   closable      True if the panel can be closed/removed
44444  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
44445  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44446  * @cfg {Toolbar}   toolbar       A toolbar for this panel
44447  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
44448  * @cfg {String} title          The title for this panel
44449  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44450  * @cfg {String} url            Calls {@link #setUrl} with this value
44451  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
44452  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
44453  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
44454  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
44455  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
44456  * @cfg {Boolean} badges render the badges
44457  * @cfg {String} cls  extra classes to use  
44458  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
44459  
44460  * @constructor
44461  * Create a new ContentPanel.
44462  * @param {String/Object} config A string to set only the title or a config object
44463  
44464  */
44465 Roo.bootstrap.panel.Content = function( config){
44466     
44467     this.tpl = config.tpl || false;
44468     
44469     var el = config.el;
44470     var content = config.content;
44471
44472     if(config.autoCreate){ // xtype is available if this is called from factory
44473         el = Roo.id();
44474     }
44475     this.el = Roo.get(el);
44476     if(!this.el && config && config.autoCreate){
44477         if(typeof config.autoCreate == "object"){
44478             if(!config.autoCreate.id){
44479                 config.autoCreate.id = config.id||el;
44480             }
44481             this.el = Roo.DomHelper.append(document.body,
44482                         config.autoCreate, true);
44483         }else{
44484             var elcfg =  {
44485                 tag: "div",
44486                 cls: (config.cls || '') +
44487                     (config.background ? ' bg-' + config.background : '') +
44488                     " roo-layout-inactive-content",
44489                 id: config.id||el
44490             };
44491             if (config.iframe) {
44492                 elcfg.cn = [
44493                     {
44494                         tag : 'iframe',
44495                         style : 'border: 0px',
44496                         src : 'about:blank'
44497                     }
44498                 ];
44499             }
44500               
44501             if (config.html) {
44502                 elcfg.html = config.html;
44503                 
44504             }
44505                         
44506             this.el = Roo.DomHelper.append(document.body, elcfg , true);
44507             if (config.iframe) {
44508                 this.iframeEl = this.el.select('iframe',true).first();
44509             }
44510             
44511         }
44512     } 
44513     this.closable = false;
44514     this.loaded = false;
44515     this.active = false;
44516    
44517       
44518     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
44519         
44520         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
44521         
44522         this.wrapEl = this.el; //this.el.wrap();
44523         var ti = [];
44524         if (config.toolbar.items) {
44525             ti = config.toolbar.items ;
44526             delete config.toolbar.items ;
44527         }
44528         
44529         var nitems = [];
44530         this.toolbar.render(this.wrapEl, 'before');
44531         for(var i =0;i < ti.length;i++) {
44532           //  Roo.log(['add child', items[i]]);
44533             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
44534         }
44535         this.toolbar.items = nitems;
44536         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
44537         delete config.toolbar;
44538         
44539     }
44540     /*
44541     // xtype created footer. - not sure if will work as we normally have to render first..
44542     if (this.footer && !this.footer.el && this.footer.xtype) {
44543         if (!this.wrapEl) {
44544             this.wrapEl = this.el.wrap();
44545         }
44546     
44547         this.footer.container = this.wrapEl.createChild();
44548          
44549         this.footer = Roo.factory(this.footer, Roo);
44550         
44551     }
44552     */
44553     
44554      if(typeof config == "string"){
44555         this.title = config;
44556     }else{
44557         Roo.apply(this, config);
44558     }
44559     
44560     if(this.resizeEl){
44561         this.resizeEl = Roo.get(this.resizeEl, true);
44562     }else{
44563         this.resizeEl = this.el;
44564     }
44565     // handle view.xtype
44566     
44567  
44568     
44569     
44570     this.addEvents({
44571         /**
44572          * @event activate
44573          * Fires when this panel is activated. 
44574          * @param {Roo.ContentPanel} this
44575          */
44576         "activate" : true,
44577         /**
44578          * @event deactivate
44579          * Fires when this panel is activated. 
44580          * @param {Roo.ContentPanel} this
44581          */
44582         "deactivate" : true,
44583
44584         /**
44585          * @event resize
44586          * Fires when this panel is resized if fitToFrame is true.
44587          * @param {Roo.ContentPanel} this
44588          * @param {Number} width The width after any component adjustments
44589          * @param {Number} height The height after any component adjustments
44590          */
44591         "resize" : true,
44592         
44593          /**
44594          * @event render
44595          * Fires when this tab is created
44596          * @param {Roo.ContentPanel} this
44597          */
44598         "render" : true,
44599         
44600           /**
44601          * @event scroll
44602          * Fires when this content is scrolled
44603          * @param {Roo.ContentPanel} this
44604          * @param {Event} scrollEvent
44605          */
44606         "scroll" : true
44607         
44608         
44609         
44610     });
44611     
44612
44613     
44614     
44615     if(this.autoScroll && !this.iframe){
44616         this.resizeEl.setStyle("overflow", "auto");
44617         this.resizeEl.on('scroll', this.onScroll, this);
44618     } else {
44619         // fix randome scrolling
44620         //this.el.on('scroll', function() {
44621         //    Roo.log('fix random scolling');
44622         //    this.scrollTo('top',0); 
44623         //});
44624     }
44625     content = content || this.content;
44626     if(content){
44627         this.setContent(content);
44628     }
44629     if(config && config.url){
44630         this.setUrl(this.url, this.params, this.loadOnce);
44631     }
44632     
44633     
44634     
44635     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
44636     
44637     if (this.view && typeof(this.view.xtype) != 'undefined') {
44638         this.view.el = this.el.appendChild(document.createElement("div"));
44639         this.view = Roo.factory(this.view); 
44640         this.view.render  &&  this.view.render(false, '');  
44641     }
44642     
44643     
44644     this.fireEvent('render', this);
44645 };
44646
44647 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
44648     
44649     cls : '',
44650     background : '',
44651     
44652     tabTip : '',
44653     
44654     iframe : false,
44655     iframeEl : false,
44656     
44657     /* Resize Element - use this to work out scroll etc. */
44658     resizeEl : false,
44659     
44660     setRegion : function(region){
44661         this.region = region;
44662         this.setActiveClass(region && !this.background);
44663     },
44664     
44665     
44666     setActiveClass: function(state)
44667     {
44668         if(state){
44669            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
44670            this.el.setStyle('position','relative');
44671         }else{
44672            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
44673            this.el.setStyle('position', 'absolute');
44674         } 
44675     },
44676     
44677     /**
44678      * Returns the toolbar for this Panel if one was configured. 
44679      * @return {Roo.Toolbar} 
44680      */
44681     getToolbar : function(){
44682         return this.toolbar;
44683     },
44684     
44685     setActiveState : function(active)
44686     {
44687         this.active = active;
44688         this.setActiveClass(active);
44689         if(!active){
44690             if(this.fireEvent("deactivate", this) === false){
44691                 return false;
44692             }
44693             return true;
44694         }
44695         this.fireEvent("activate", this);
44696         return true;
44697     },
44698     /**
44699      * Updates this panel's element (not for iframe)
44700      * @param {String} content The new content
44701      * @param {Boolean} loadScripts (optional) true to look for and process scripts
44702     */
44703     setContent : function(content, loadScripts){
44704         if (this.iframe) {
44705             return;
44706         }
44707         
44708         this.el.update(content, loadScripts);
44709     },
44710
44711     ignoreResize : function(w, h)
44712     {
44713         //return false; // always resize?
44714         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
44715             return true;
44716         }else{
44717             this.lastSize = {width: w, height: h};
44718             return false;
44719         }
44720     },
44721     /**
44722      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
44723      * @return {Roo.UpdateManager} The UpdateManager
44724      */
44725     getUpdateManager : function(){
44726         if (this.iframe) {
44727             return false;
44728         }
44729         return this.el.getUpdateManager();
44730     },
44731      /**
44732      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
44733      * Does not work with IFRAME contents
44734      * @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:
44735 <pre><code>
44736 panel.load({
44737     url: "your-url.php",
44738     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
44739     callback: yourFunction,
44740     scope: yourObject, //(optional scope)
44741     discardUrl: false,
44742     nocache: false,
44743     text: "Loading...",
44744     timeout: 30,
44745     scripts: false
44746 });
44747 </code></pre>
44748      
44749      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
44750      * 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.
44751      * @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}
44752      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
44753      * @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.
44754      * @return {Roo.ContentPanel} this
44755      */
44756     load : function(){
44757         
44758         if (this.iframe) {
44759             return this;
44760         }
44761         
44762         var um = this.el.getUpdateManager();
44763         um.update.apply(um, arguments);
44764         return this;
44765     },
44766
44767
44768     /**
44769      * 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.
44770      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
44771      * @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)
44772      * @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)
44773      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
44774      */
44775     setUrl : function(url, params, loadOnce){
44776         if (this.iframe) {
44777             this.iframeEl.dom.src = url;
44778             return false;
44779         }
44780         
44781         if(this.refreshDelegate){
44782             this.removeListener("activate", this.refreshDelegate);
44783         }
44784         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44785         this.on("activate", this.refreshDelegate);
44786         return this.el.getUpdateManager();
44787     },
44788     
44789     _handleRefresh : function(url, params, loadOnce){
44790         if(!loadOnce || !this.loaded){
44791             var updater = this.el.getUpdateManager();
44792             updater.update(url, params, this._setLoaded.createDelegate(this));
44793         }
44794     },
44795     
44796     _setLoaded : function(){
44797         this.loaded = true;
44798     }, 
44799     
44800     /**
44801      * Returns this panel's id
44802      * @return {String} 
44803      */
44804     getId : function(){
44805         return this.el.id;
44806     },
44807     
44808     /** 
44809      * Returns this panel's element - used by regiosn to add.
44810      * @return {Roo.Element} 
44811      */
44812     getEl : function(){
44813         return this.wrapEl || this.el;
44814     },
44815     
44816    
44817     
44818     adjustForComponents : function(width, height)
44819     {
44820         //Roo.log('adjustForComponents ');
44821         if(this.resizeEl != this.el){
44822             width -= this.el.getFrameWidth('lr');
44823             height -= this.el.getFrameWidth('tb');
44824         }
44825         if(this.toolbar){
44826             var te = this.toolbar.getEl();
44827             te.setWidth(width);
44828             height -= te.getHeight();
44829         }
44830         if(this.footer){
44831             var te = this.footer.getEl();
44832             te.setWidth(width);
44833             height -= te.getHeight();
44834         }
44835         
44836         
44837         if(this.adjustments){
44838             width += this.adjustments[0];
44839             height += this.adjustments[1];
44840         }
44841         return {"width": width, "height": height};
44842     },
44843     
44844     setSize : function(width, height){
44845         if(this.fitToFrame && !this.ignoreResize(width, height)){
44846             if(this.fitContainer && this.resizeEl != this.el){
44847                 this.el.setSize(width, height);
44848             }
44849             var size = this.adjustForComponents(width, height);
44850             if (this.iframe) {
44851                 this.iframeEl.setSize(width,height);
44852             }
44853             
44854             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
44855             this.fireEvent('resize', this, size.width, size.height);
44856             
44857             
44858         }
44859     },
44860     
44861     /**
44862      * Returns this panel's title
44863      * @return {String} 
44864      */
44865     getTitle : function(){
44866         
44867         if (typeof(this.title) != 'object') {
44868             return this.title;
44869         }
44870         
44871         var t = '';
44872         for (var k in this.title) {
44873             if (!this.title.hasOwnProperty(k)) {
44874                 continue;
44875             }
44876             
44877             if (k.indexOf('-') >= 0) {
44878                 var s = k.split('-');
44879                 for (var i = 0; i<s.length; i++) {
44880                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
44881                 }
44882             } else {
44883                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
44884             }
44885         }
44886         return t;
44887     },
44888     
44889     /**
44890      * Set this panel's title
44891      * @param {String} title
44892      */
44893     setTitle : function(title){
44894         this.title = title;
44895         if(this.region){
44896             this.region.updatePanelTitle(this, title);
44897         }
44898     },
44899     
44900     /**
44901      * Returns true is this panel was configured to be closable
44902      * @return {Boolean} 
44903      */
44904     isClosable : function(){
44905         return this.closable;
44906     },
44907     
44908     beforeSlide : function(){
44909         this.el.clip();
44910         this.resizeEl.clip();
44911     },
44912     
44913     afterSlide : function(){
44914         this.el.unclip();
44915         this.resizeEl.unclip();
44916     },
44917     
44918     /**
44919      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
44920      *   Will fail silently if the {@link #setUrl} method has not been called.
44921      *   This does not activate the panel, just updates its content.
44922      */
44923     refresh : function(){
44924         if(this.refreshDelegate){
44925            this.loaded = false;
44926            this.refreshDelegate();
44927         }
44928     },
44929     
44930     /**
44931      * Destroys this panel
44932      */
44933     destroy : function(){
44934         this.el.removeAllListeners();
44935         var tempEl = document.createElement("span");
44936         tempEl.appendChild(this.el.dom);
44937         tempEl.innerHTML = "";
44938         this.el.remove();
44939         this.el = null;
44940     },
44941     
44942     /**
44943      * form - if the content panel contains a form - this is a reference to it.
44944      * @type {Roo.form.Form}
44945      */
44946     form : false,
44947     /**
44948      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
44949      *    This contains a reference to it.
44950      * @type {Roo.View}
44951      */
44952     view : false,
44953     
44954       /**
44955      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
44956      * <pre><code>
44957
44958 layout.addxtype({
44959        xtype : 'Form',
44960        items: [ .... ]
44961    }
44962 );
44963
44964 </code></pre>
44965      * @param {Object} cfg Xtype definition of item to add.
44966      */
44967     
44968     
44969     getChildContainer: function () {
44970         return this.getEl();
44971     },
44972     
44973     
44974     onScroll : function(e)
44975     {
44976         this.fireEvent('scroll', this, e);
44977     }
44978     
44979     
44980     /*
44981         var  ret = new Roo.factory(cfg);
44982         return ret;
44983         
44984         
44985         // add form..
44986         if (cfg.xtype.match(/^Form$/)) {
44987             
44988             var el;
44989             //if (this.footer) {
44990             //    el = this.footer.container.insertSibling(false, 'before');
44991             //} else {
44992                 el = this.el.createChild();
44993             //}
44994
44995             this.form = new  Roo.form.Form(cfg);
44996             
44997             
44998             if ( this.form.allItems.length) {
44999                 this.form.render(el.dom);
45000             }
45001             return this.form;
45002         }
45003         // should only have one of theses..
45004         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45005             // views.. should not be just added - used named prop 'view''
45006             
45007             cfg.el = this.el.appendChild(document.createElement("div"));
45008             // factory?
45009             
45010             var ret = new Roo.factory(cfg);
45011              
45012              ret.render && ret.render(false, ''); // render blank..
45013             this.view = ret;
45014             return ret;
45015         }
45016         return false;
45017     }
45018     \*/
45019 });
45020  
45021 /**
45022  * @class Roo.bootstrap.panel.Grid
45023  * @extends Roo.bootstrap.panel.Content
45024  * @constructor
45025  * Create a new GridPanel.
45026  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45027  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45028  * @param {Object} config A the config object
45029   
45030  */
45031
45032
45033
45034 Roo.bootstrap.panel.Grid = function(config)
45035 {
45036     
45037       
45038     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45039         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45040
45041     config.el = this.wrapper;
45042     //this.el = this.wrapper;
45043     
45044       if (config.container) {
45045         // ctor'ed from a Border/panel.grid
45046         
45047         
45048         this.wrapper.setStyle("overflow", "hidden");
45049         this.wrapper.addClass('roo-grid-container');
45050
45051     }
45052     
45053     
45054     if(config.toolbar){
45055         var tool_el = this.wrapper.createChild();    
45056         this.toolbar = Roo.factory(config.toolbar);
45057         var ti = [];
45058         if (config.toolbar.items) {
45059             ti = config.toolbar.items ;
45060             delete config.toolbar.items ;
45061         }
45062         
45063         var nitems = [];
45064         this.toolbar.render(tool_el);
45065         for(var i =0;i < ti.length;i++) {
45066           //  Roo.log(['add child', items[i]]);
45067             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45068         }
45069         this.toolbar.items = nitems;
45070         
45071         delete config.toolbar;
45072     }
45073     
45074     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45075     config.grid.scrollBody = true;;
45076     config.grid.monitorWindowResize = false; // turn off autosizing
45077     config.grid.autoHeight = false;
45078     config.grid.autoWidth = false;
45079     
45080     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45081     
45082     if (config.background) {
45083         // render grid on panel activation (if panel background)
45084         this.on('activate', function(gp) {
45085             if (!gp.grid.rendered) {
45086                 gp.grid.render(this.wrapper);
45087                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
45088             }
45089         });
45090             
45091     } else {
45092         this.grid.render(this.wrapper);
45093         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
45094
45095     }
45096     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45097     // ??? needed ??? config.el = this.wrapper;
45098     
45099     
45100     
45101   
45102     // xtype created footer. - not sure if will work as we normally have to render first..
45103     if (this.footer && !this.footer.el && this.footer.xtype) {
45104         
45105         var ctr = this.grid.getView().getFooterPanel(true);
45106         this.footer.dataSource = this.grid.dataSource;
45107         this.footer = Roo.factory(this.footer, Roo);
45108         this.footer.render(ctr);
45109         
45110     }
45111     
45112     
45113     
45114     
45115      
45116 };
45117
45118 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45119 {
45120   
45121     getId : function(){
45122         return this.grid.id;
45123     },
45124     
45125     /**
45126      * Returns the grid for this panel
45127      * @return {Roo.bootstrap.Table} 
45128      */
45129     getGrid : function(){
45130         return this.grid;    
45131     },
45132     
45133     setSize : function(width, height)
45134     {
45135      
45136         //if(!this.ignoreResize(width, height)){
45137             var grid = this.grid;
45138             var size = this.adjustForComponents(width, height);
45139             // tfoot is not a footer?
45140           
45141             
45142             var gridel = grid.getGridEl();
45143             gridel.setSize(size.width, size.height);
45144             
45145             var tbd = grid.getGridEl().select('tbody', true).first();
45146             var thd = grid.getGridEl().select('thead',true).first();
45147             var tbf= grid.getGridEl().select('tfoot', true).first();
45148
45149             if (tbf) {
45150                 size.height -= tbf.getHeight();
45151             }
45152             if (thd) {
45153                 size.height -= thd.getHeight();
45154             }
45155             
45156             tbd.setSize(size.width, size.height );
45157             // this is for the account management tab -seems to work there.
45158             var thd = grid.getGridEl().select('thead',true).first();
45159             //if (tbd) {
45160             //    tbd.setSize(size.width, size.height - thd.getHeight());
45161             //}
45162              
45163             grid.autoSize();
45164         //}
45165    
45166     },
45167      
45168     
45169     
45170     beforeSlide : function(){
45171         this.grid.getView().scroller.clip();
45172     },
45173     
45174     afterSlide : function(){
45175         this.grid.getView().scroller.unclip();
45176     },
45177     
45178     destroy : function(){
45179         this.grid.destroy();
45180         delete this.grid;
45181         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
45182     }
45183 });
45184
45185 /**
45186  * @class Roo.bootstrap.panel.Nest
45187  * @extends Roo.bootstrap.panel.Content
45188  * @constructor
45189  * Create a new Panel, that can contain a layout.Border.
45190  * 
45191  * 
45192  * @param {String/Object} config A string to set only the title or a config object
45193  */
45194 Roo.bootstrap.panel.Nest = function(config)
45195 {
45196     // construct with only one argument..
45197     /* FIXME - implement nicer consturctors
45198     if (layout.layout) {
45199         config = layout;
45200         layout = config.layout;
45201         delete config.layout;
45202     }
45203     if (layout.xtype && !layout.getEl) {
45204         // then layout needs constructing..
45205         layout = Roo.factory(layout, Roo);
45206     }
45207     */
45208     
45209     config.el =  config.layout.getEl();
45210     
45211     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
45212     
45213     config.layout.monitorWindowResize = false; // turn off autosizing
45214     this.layout = config.layout;
45215     this.layout.getEl().addClass("roo-layout-nested-layout");
45216     this.layout.parent = this;
45217     
45218     
45219     
45220     
45221 };
45222
45223 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
45224     /**
45225     * @cfg {Roo.BorderLayout} layout The layout for this panel
45226     */
45227     layout : false,
45228
45229     setSize : function(width, height){
45230         if(!this.ignoreResize(width, height)){
45231             var size = this.adjustForComponents(width, height);
45232             var el = this.layout.getEl();
45233             if (size.height < 1) {
45234                 el.setWidth(size.width);   
45235             } else {
45236                 el.setSize(size.width, size.height);
45237             }
45238             var touch = el.dom.offsetWidth;
45239             this.layout.layout();
45240             // ie requires a double layout on the first pass
45241             if(Roo.isIE && !this.initialized){
45242                 this.initialized = true;
45243                 this.layout.layout();
45244             }
45245         }
45246     },
45247     
45248     // activate all subpanels if not currently active..
45249     
45250     setActiveState : function(active){
45251         this.active = active;
45252         this.setActiveClass(active);
45253         
45254         if(!active){
45255             this.fireEvent("deactivate", this);
45256             return;
45257         }
45258         
45259         this.fireEvent("activate", this);
45260         // not sure if this should happen before or after..
45261         if (!this.layout) {
45262             return; // should not happen..
45263         }
45264         var reg = false;
45265         for (var r in this.layout.regions) {
45266             reg = this.layout.getRegion(r);
45267             if (reg.getActivePanel()) {
45268                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
45269                 reg.setActivePanel(reg.getActivePanel());
45270                 continue;
45271             }
45272             if (!reg.panels.length) {
45273                 continue;
45274             }
45275             reg.showPanel(reg.getPanel(0));
45276         }
45277         
45278         
45279         
45280         
45281     },
45282     
45283     /**
45284      * Returns the nested BorderLayout for this panel
45285      * @return {Roo.BorderLayout} 
45286      */
45287     getLayout : function(){
45288         return this.layout;
45289     },
45290     
45291      /**
45292      * Adds a xtype elements to the layout of the nested panel
45293      * <pre><code>
45294
45295 panel.addxtype({
45296        xtype : 'ContentPanel',
45297        region: 'west',
45298        items: [ .... ]
45299    }
45300 );
45301
45302 panel.addxtype({
45303         xtype : 'NestedLayoutPanel',
45304         region: 'west',
45305         layout: {
45306            center: { },
45307            west: { }   
45308         },
45309         items : [ ... list of content panels or nested layout panels.. ]
45310    }
45311 );
45312 </code></pre>
45313      * @param {Object} cfg Xtype definition of item to add.
45314      */
45315     addxtype : function(cfg) {
45316         return this.layout.addxtype(cfg);
45317     
45318     }
45319 });/*
45320  * Based on:
45321  * Ext JS Library 1.1.1
45322  * Copyright(c) 2006-2007, Ext JS, LLC.
45323  *
45324  * Originally Released Under LGPL - original licence link has changed is not relivant.
45325  *
45326  * Fork - LGPL
45327  * <script type="text/javascript">
45328  */
45329 /**
45330  * @class Roo.TabPanel
45331  * @extends Roo.util.Observable
45332  * A lightweight tab container.
45333  * <br><br>
45334  * Usage:
45335  * <pre><code>
45336 // basic tabs 1, built from existing content
45337 var tabs = new Roo.TabPanel("tabs1");
45338 tabs.addTab("script", "View Script");
45339 tabs.addTab("markup", "View Markup");
45340 tabs.activate("script");
45341
45342 // more advanced tabs, built from javascript
45343 var jtabs = new Roo.TabPanel("jtabs");
45344 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
45345
45346 // set up the UpdateManager
45347 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
45348 var updater = tab2.getUpdateManager();
45349 updater.setDefaultUrl("ajax1.htm");
45350 tab2.on('activate', updater.refresh, updater, true);
45351
45352 // Use setUrl for Ajax loading
45353 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
45354 tab3.setUrl("ajax2.htm", null, true);
45355
45356 // Disabled tab
45357 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
45358 tab4.disable();
45359
45360 jtabs.activate("jtabs-1");
45361  * </code></pre>
45362  * @constructor
45363  * Create a new TabPanel.
45364  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
45365  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
45366  */
45367 Roo.bootstrap.panel.Tabs = function(config){
45368     /**
45369     * The container element for this TabPanel.
45370     * @type Roo.Element
45371     */
45372     this.el = Roo.get(config.el);
45373     delete config.el;
45374     if(config){
45375         if(typeof config == "boolean"){
45376             this.tabPosition = config ? "bottom" : "top";
45377         }else{
45378             Roo.apply(this, config);
45379         }
45380     }
45381     
45382     if(this.tabPosition == "bottom"){
45383         // if tabs are at the bottom = create the body first.
45384         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45385         this.el.addClass("roo-tabs-bottom");
45386     }
45387     // next create the tabs holders
45388     
45389     if (this.tabPosition == "west"){
45390         
45391         var reg = this.region; // fake it..
45392         while (reg) {
45393             if (!reg.mgr.parent) {
45394                 break;
45395             }
45396             reg = reg.mgr.parent.region;
45397         }
45398         Roo.log("got nest?");
45399         Roo.log(reg);
45400         if (reg.mgr.getRegion('west')) {
45401             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
45402             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
45403             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45404             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45405             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45406         
45407             
45408         }
45409         
45410         
45411     } else {
45412      
45413         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
45414         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45415         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45416         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45417     }
45418     
45419     
45420     if(Roo.isIE){
45421         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
45422     }
45423     
45424     // finally - if tabs are at the top, then create the body last..
45425     if(this.tabPosition != "bottom"){
45426         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
45427          * @type Roo.Element
45428          */
45429         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45430         this.el.addClass("roo-tabs-top");
45431     }
45432     this.items = [];
45433
45434     this.bodyEl.setStyle("position", "relative");
45435
45436     this.active = null;
45437     this.activateDelegate = this.activate.createDelegate(this);
45438
45439     this.addEvents({
45440         /**
45441          * @event tabchange
45442          * Fires when the active tab changes
45443          * @param {Roo.TabPanel} this
45444          * @param {Roo.TabPanelItem} activePanel The new active tab
45445          */
45446         "tabchange": true,
45447         /**
45448          * @event beforetabchange
45449          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
45450          * @param {Roo.TabPanel} this
45451          * @param {Object} e Set cancel to true on this object to cancel the tab change
45452          * @param {Roo.TabPanelItem} tab The tab being changed to
45453          */
45454         "beforetabchange" : true
45455     });
45456
45457     Roo.EventManager.onWindowResize(this.onResize, this);
45458     this.cpad = this.el.getPadding("lr");
45459     this.hiddenCount = 0;
45460
45461
45462     // toolbar on the tabbar support...
45463     if (this.toolbar) {
45464         alert("no toolbar support yet");
45465         this.toolbar  = false;
45466         /*
45467         var tcfg = this.toolbar;
45468         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
45469         this.toolbar = new Roo.Toolbar(tcfg);
45470         if (Roo.isSafari) {
45471             var tbl = tcfg.container.child('table', true);
45472             tbl.setAttribute('width', '100%');
45473         }
45474         */
45475         
45476     }
45477    
45478
45479
45480     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
45481 };
45482
45483 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
45484     /*
45485      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
45486      */
45487     tabPosition : "top",
45488     /*
45489      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
45490      */
45491     currentTabWidth : 0,
45492     /*
45493      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
45494      */
45495     minTabWidth : 40,
45496     /*
45497      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
45498      */
45499     maxTabWidth : 250,
45500     /*
45501      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
45502      */
45503     preferredTabWidth : 175,
45504     /*
45505      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
45506      */
45507     resizeTabs : false,
45508     /*
45509      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
45510      */
45511     monitorResize : true,
45512     /*
45513      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
45514      */
45515     toolbar : false,  // set by caller..
45516     
45517     region : false, /// set by caller
45518     
45519     disableTooltips : true, // not used yet...
45520
45521     /**
45522      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
45523      * @param {String} id The id of the div to use <b>or create</b>
45524      * @param {String} text The text for the tab
45525      * @param {String} content (optional) Content to put in the TabPanelItem body
45526      * @param {Boolean} closable (optional) True to create a close icon on the tab
45527      * @return {Roo.TabPanelItem} The created TabPanelItem
45528      */
45529     addTab : function(id, text, content, closable, tpl)
45530     {
45531         var item = new Roo.bootstrap.panel.TabItem({
45532             panel: this,
45533             id : id,
45534             text : text,
45535             closable : closable,
45536             tpl : tpl
45537         });
45538         this.addTabItem(item);
45539         if(content){
45540             item.setContent(content);
45541         }
45542         return item;
45543     },
45544
45545     /**
45546      * Returns the {@link Roo.TabPanelItem} with the specified id/index
45547      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
45548      * @return {Roo.TabPanelItem}
45549      */
45550     getTab : function(id){
45551         return this.items[id];
45552     },
45553
45554     /**
45555      * Hides the {@link Roo.TabPanelItem} with the specified id/index
45556      * @param {String/Number} id The id or index of the TabPanelItem to hide.
45557      */
45558     hideTab : function(id){
45559         var t = this.items[id];
45560         if(!t.isHidden()){
45561            t.setHidden(true);
45562            this.hiddenCount++;
45563            this.autoSizeTabs();
45564         }
45565     },
45566
45567     /**
45568      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
45569      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
45570      */
45571     unhideTab : function(id){
45572         var t = this.items[id];
45573         if(t.isHidden()){
45574            t.setHidden(false);
45575            this.hiddenCount--;
45576            this.autoSizeTabs();
45577         }
45578     },
45579
45580     /**
45581      * Adds an existing {@link Roo.TabPanelItem}.
45582      * @param {Roo.TabPanelItem} item The TabPanelItem to add
45583      */
45584     addTabItem : function(item)
45585     {
45586         this.items[item.id] = item;
45587         this.items.push(item);
45588         this.autoSizeTabs();
45589       //  if(this.resizeTabs){
45590     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
45591   //         this.autoSizeTabs();
45592 //        }else{
45593 //            item.autoSize();
45594        // }
45595     },
45596
45597     /**
45598      * Removes a {@link Roo.TabPanelItem}.
45599      * @param {String/Number} id The id or index of the TabPanelItem to remove.
45600      */
45601     removeTab : function(id){
45602         var items = this.items;
45603         var tab = items[id];
45604         if(!tab) { return; }
45605         var index = items.indexOf(tab);
45606         if(this.active == tab && items.length > 1){
45607             var newTab = this.getNextAvailable(index);
45608             if(newTab) {
45609                 newTab.activate();
45610             }
45611         }
45612         this.stripEl.dom.removeChild(tab.pnode.dom);
45613         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
45614             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
45615         }
45616         items.splice(index, 1);
45617         delete this.items[tab.id];
45618         tab.fireEvent("close", tab);
45619         tab.purgeListeners();
45620         this.autoSizeTabs();
45621     },
45622
45623     getNextAvailable : function(start){
45624         var items = this.items;
45625         var index = start;
45626         // look for a next tab that will slide over to
45627         // replace the one being removed
45628         while(index < items.length){
45629             var item = items[++index];
45630             if(item && !item.isHidden()){
45631                 return item;
45632             }
45633         }
45634         // if one isn't found select the previous tab (on the left)
45635         index = start;
45636         while(index >= 0){
45637             var item = items[--index];
45638             if(item && !item.isHidden()){
45639                 return item;
45640             }
45641         }
45642         return null;
45643     },
45644
45645     /**
45646      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
45647      * @param {String/Number} id The id or index of the TabPanelItem to disable.
45648      */
45649     disableTab : function(id){
45650         var tab = this.items[id];
45651         if(tab && this.active != tab){
45652             tab.disable();
45653         }
45654     },
45655
45656     /**
45657      * Enables a {@link Roo.TabPanelItem} that is disabled.
45658      * @param {String/Number} id The id or index of the TabPanelItem to enable.
45659      */
45660     enableTab : function(id){
45661         var tab = this.items[id];
45662         tab.enable();
45663     },
45664
45665     /**
45666      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
45667      * @param {String/Number} id The id or index of the TabPanelItem to activate.
45668      * @return {Roo.TabPanelItem} The TabPanelItem.
45669      */
45670     activate : function(id)
45671     {
45672         //Roo.log('activite:'  + id);
45673         
45674         var tab = this.items[id];
45675         if(!tab){
45676             return null;
45677         }
45678         if(tab == this.active || tab.disabled){
45679             return tab;
45680         }
45681         var e = {};
45682         this.fireEvent("beforetabchange", this, e, tab);
45683         if(e.cancel !== true && !tab.disabled){
45684             if(this.active){
45685                 this.active.hide();
45686             }
45687             this.active = this.items[id];
45688             this.active.show();
45689             this.fireEvent("tabchange", this, this.active);
45690         }
45691         return tab;
45692     },
45693
45694     /**
45695      * Gets the active {@link Roo.TabPanelItem}.
45696      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
45697      */
45698     getActiveTab : function(){
45699         return this.active;
45700     },
45701
45702     /**
45703      * Updates the tab body element to fit the height of the container element
45704      * for overflow scrolling
45705      * @param {Number} targetHeight (optional) Override the starting height from the elements height
45706      */
45707     syncHeight : function(targetHeight){
45708         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
45709         var bm = this.bodyEl.getMargins();
45710         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
45711         this.bodyEl.setHeight(newHeight);
45712         return newHeight;
45713     },
45714
45715     onResize : function(){
45716         if(this.monitorResize){
45717             this.autoSizeTabs();
45718         }
45719     },
45720
45721     /**
45722      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
45723      */
45724     beginUpdate : function(){
45725         this.updating = true;
45726     },
45727
45728     /**
45729      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
45730      */
45731     endUpdate : function(){
45732         this.updating = false;
45733         this.autoSizeTabs();
45734     },
45735
45736     /**
45737      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
45738      */
45739     autoSizeTabs : function()
45740     {
45741         var count = this.items.length;
45742         var vcount = count - this.hiddenCount;
45743         
45744         if (vcount < 2) {
45745             this.stripEl.hide();
45746         } else {
45747             this.stripEl.show();
45748         }
45749         
45750         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
45751             return;
45752         }
45753         
45754         
45755         var w = Math.max(this.el.getWidth() - this.cpad, 10);
45756         var availWidth = Math.floor(w / vcount);
45757         var b = this.stripBody;
45758         if(b.getWidth() > w){
45759             var tabs = this.items;
45760             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
45761             if(availWidth < this.minTabWidth){
45762                 /*if(!this.sleft){    // incomplete scrolling code
45763                     this.createScrollButtons();
45764                 }
45765                 this.showScroll();
45766                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
45767             }
45768         }else{
45769             if(this.currentTabWidth < this.preferredTabWidth){
45770                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
45771             }
45772         }
45773     },
45774
45775     /**
45776      * Returns the number of tabs in this TabPanel.
45777      * @return {Number}
45778      */
45779      getCount : function(){
45780          return this.items.length;
45781      },
45782
45783     /**
45784      * Resizes all the tabs to the passed width
45785      * @param {Number} The new width
45786      */
45787     setTabWidth : function(width){
45788         this.currentTabWidth = width;
45789         for(var i = 0, len = this.items.length; i < len; i++) {
45790                 if(!this.items[i].isHidden()) {
45791                 this.items[i].setWidth(width);
45792             }
45793         }
45794     },
45795
45796     /**
45797      * Destroys this TabPanel
45798      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
45799      */
45800     destroy : function(removeEl){
45801         Roo.EventManager.removeResizeListener(this.onResize, this);
45802         for(var i = 0, len = this.items.length; i < len; i++){
45803             this.items[i].purgeListeners();
45804         }
45805         if(removeEl === true){
45806             this.el.update("");
45807             this.el.remove();
45808         }
45809     },
45810     
45811     createStrip : function(container)
45812     {
45813         var strip = document.createElement("nav");
45814         strip.className = Roo.bootstrap.version == 4 ?
45815             "navbar-light bg-light" : 
45816             "navbar navbar-default"; //"x-tabs-wrap";
45817         container.appendChild(strip);
45818         return strip;
45819     },
45820     
45821     createStripList : function(strip)
45822     {
45823         // div wrapper for retard IE
45824         // returns the "tr" element.
45825         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
45826         //'<div class="x-tabs-strip-wrap">'+
45827           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
45828           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
45829         return strip.firstChild; //.firstChild.firstChild.firstChild;
45830     },
45831     createBody : function(container)
45832     {
45833         var body = document.createElement("div");
45834         Roo.id(body, "tab-body");
45835         //Roo.fly(body).addClass("x-tabs-body");
45836         Roo.fly(body).addClass("tab-content");
45837         container.appendChild(body);
45838         return body;
45839     },
45840     createItemBody :function(bodyEl, id){
45841         var body = Roo.getDom(id);
45842         if(!body){
45843             body = document.createElement("div");
45844             body.id = id;
45845         }
45846         //Roo.fly(body).addClass("x-tabs-item-body");
45847         Roo.fly(body).addClass("tab-pane");
45848          bodyEl.insertBefore(body, bodyEl.firstChild);
45849         return body;
45850     },
45851     /** @private */
45852     createStripElements :  function(stripEl, text, closable, tpl)
45853     {
45854         var td = document.createElement("li"); // was td..
45855         td.className = 'nav-item';
45856         
45857         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
45858         
45859         
45860         stripEl.appendChild(td);
45861         /*if(closable){
45862             td.className = "x-tabs-closable";
45863             if(!this.closeTpl){
45864                 this.closeTpl = new Roo.Template(
45865                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45866                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
45867                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
45868                 );
45869             }
45870             var el = this.closeTpl.overwrite(td, {"text": text});
45871             var close = el.getElementsByTagName("div")[0];
45872             var inner = el.getElementsByTagName("em")[0];
45873             return {"el": el, "close": close, "inner": inner};
45874         } else {
45875         */
45876         // not sure what this is..
45877 //            if(!this.tabTpl){
45878                 //this.tabTpl = new Roo.Template(
45879                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45880                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
45881                 //);
45882 //                this.tabTpl = new Roo.Template(
45883 //                   '<a href="#">' +
45884 //                   '<span unselectable="on"' +
45885 //                            (this.disableTooltips ? '' : ' title="{text}"') +
45886 //                            ' >{text}</span></a>'
45887 //                );
45888 //                
45889 //            }
45890
45891
45892             var template = tpl || this.tabTpl || false;
45893             
45894             if(!template){
45895                 template =  new Roo.Template(
45896                         Roo.bootstrap.version == 4 ? 
45897                             (
45898                                 '<a class="nav-link" href="#" unselectable="on"' +
45899                                      (this.disableTooltips ? '' : ' title="{text}"') +
45900                                      ' >{text}</a>'
45901                             ) : (
45902                                 '<a class="nav-link" href="#">' +
45903                                 '<span unselectable="on"' +
45904                                          (this.disableTooltips ? '' : ' title="{text}"') +
45905                                     ' >{text}</span></a>'
45906                             )
45907                 );
45908             }
45909             
45910             switch (typeof(template)) {
45911                 case 'object' :
45912                     break;
45913                 case 'string' :
45914                     template = new Roo.Template(template);
45915                     break;
45916                 default :
45917                     break;
45918             }
45919             
45920             var el = template.overwrite(td, {"text": text});
45921             
45922             var inner = el.getElementsByTagName("span")[0];
45923             
45924             return {"el": el, "inner": inner};
45925             
45926     }
45927         
45928     
45929 });
45930
45931 /**
45932  * @class Roo.TabPanelItem
45933  * @extends Roo.util.Observable
45934  * Represents an individual item (tab plus body) in a TabPanel.
45935  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
45936  * @param {String} id The id of this TabPanelItem
45937  * @param {String} text The text for the tab of this TabPanelItem
45938  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
45939  */
45940 Roo.bootstrap.panel.TabItem = function(config){
45941     /**
45942      * The {@link Roo.TabPanel} this TabPanelItem belongs to
45943      * @type Roo.TabPanel
45944      */
45945     this.tabPanel = config.panel;
45946     /**
45947      * The id for this TabPanelItem
45948      * @type String
45949      */
45950     this.id = config.id;
45951     /** @private */
45952     this.disabled = false;
45953     /** @private */
45954     this.text = config.text;
45955     /** @private */
45956     this.loaded = false;
45957     this.closable = config.closable;
45958
45959     /**
45960      * The body element for this TabPanelItem.
45961      * @type Roo.Element
45962      */
45963     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
45964     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
45965     this.bodyEl.setStyle("display", "block");
45966     this.bodyEl.setStyle("zoom", "1");
45967     //this.hideAction();
45968
45969     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
45970     /** @private */
45971     this.el = Roo.get(els.el);
45972     this.inner = Roo.get(els.inner, true);
45973      this.textEl = Roo.bootstrap.version == 4 ?
45974         this.el : Roo.get(this.el.dom.firstChild, true);
45975
45976     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
45977     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
45978
45979     
45980 //    this.el.on("mousedown", this.onTabMouseDown, this);
45981     this.el.on("click", this.onTabClick, this);
45982     /** @private */
45983     if(config.closable){
45984         var c = Roo.get(els.close, true);
45985         c.dom.title = this.closeText;
45986         c.addClassOnOver("close-over");
45987         c.on("click", this.closeClick, this);
45988      }
45989
45990     this.addEvents({
45991          /**
45992          * @event activate
45993          * Fires when this tab becomes the active tab.
45994          * @param {Roo.TabPanel} tabPanel The parent TabPanel
45995          * @param {Roo.TabPanelItem} this
45996          */
45997         "activate": true,
45998         /**
45999          * @event beforeclose
46000          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46001          * @param {Roo.TabPanelItem} this
46002          * @param {Object} e Set cancel to true on this object to cancel the close.
46003          */
46004         "beforeclose": true,
46005         /**
46006          * @event close
46007          * Fires when this tab is closed.
46008          * @param {Roo.TabPanelItem} this
46009          */
46010          "close": true,
46011         /**
46012          * @event deactivate
46013          * Fires when this tab is no longer the active tab.
46014          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46015          * @param {Roo.TabPanelItem} this
46016          */
46017          "deactivate" : true
46018     });
46019     this.hidden = false;
46020
46021     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46022 };
46023
46024 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46025            {
46026     purgeListeners : function(){
46027        Roo.util.Observable.prototype.purgeListeners.call(this);
46028        this.el.removeAllListeners();
46029     },
46030     /**
46031      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46032      */
46033     show : function(){
46034         this.status_node.addClass("active");
46035         this.showAction();
46036         if(Roo.isOpera){
46037             this.tabPanel.stripWrap.repaint();
46038         }
46039         this.fireEvent("activate", this.tabPanel, this);
46040     },
46041
46042     /**
46043      * Returns true if this tab is the active tab.
46044      * @return {Boolean}
46045      */
46046     isActive : function(){
46047         return this.tabPanel.getActiveTab() == this;
46048     },
46049
46050     /**
46051      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46052      */
46053     hide : function(){
46054         this.status_node.removeClass("active");
46055         this.hideAction();
46056         this.fireEvent("deactivate", this.tabPanel, this);
46057     },
46058
46059     hideAction : function(){
46060         this.bodyEl.hide();
46061         this.bodyEl.setStyle("position", "absolute");
46062         this.bodyEl.setLeft("-20000px");
46063         this.bodyEl.setTop("-20000px");
46064     },
46065
46066     showAction : function(){
46067         this.bodyEl.setStyle("position", "relative");
46068         this.bodyEl.setTop("");
46069         this.bodyEl.setLeft("");
46070         this.bodyEl.show();
46071     },
46072
46073     /**
46074      * Set the tooltip for the tab.
46075      * @param {String} tooltip The tab's tooltip
46076      */
46077     setTooltip : function(text){
46078         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46079             this.textEl.dom.qtip = text;
46080             this.textEl.dom.removeAttribute('title');
46081         }else{
46082             this.textEl.dom.title = text;
46083         }
46084     },
46085
46086     onTabClick : function(e){
46087         e.preventDefault();
46088         this.tabPanel.activate(this.id);
46089     },
46090
46091     onTabMouseDown : function(e){
46092         e.preventDefault();
46093         this.tabPanel.activate(this.id);
46094     },
46095 /*
46096     getWidth : function(){
46097         return this.inner.getWidth();
46098     },
46099
46100     setWidth : function(width){
46101         var iwidth = width - this.linode.getPadding("lr");
46102         this.inner.setWidth(iwidth);
46103         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46104         this.linode.setWidth(width);
46105     },
46106 */
46107     /**
46108      * Show or hide the tab
46109      * @param {Boolean} hidden True to hide or false to show.
46110      */
46111     setHidden : function(hidden){
46112         this.hidden = hidden;
46113         this.linode.setStyle("display", hidden ? "none" : "");
46114     },
46115
46116     /**
46117      * Returns true if this tab is "hidden"
46118      * @return {Boolean}
46119      */
46120     isHidden : function(){
46121         return this.hidden;
46122     },
46123
46124     /**
46125      * Returns the text for this tab
46126      * @return {String}
46127      */
46128     getText : function(){
46129         return this.text;
46130     },
46131     /*
46132     autoSize : function(){
46133         //this.el.beginMeasure();
46134         this.textEl.setWidth(1);
46135         /*
46136          *  #2804 [new] Tabs in Roojs
46137          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46138          */
46139         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46140         //this.el.endMeasure();
46141     //},
46142
46143     /**
46144      * Sets the text for the tab (Note: this also sets the tooltip text)
46145      * @param {String} text The tab's text and tooltip
46146      */
46147     setText : function(text){
46148         this.text = text;
46149         this.textEl.update(text);
46150         this.setTooltip(text);
46151         //if(!this.tabPanel.resizeTabs){
46152         //    this.autoSize();
46153         //}
46154     },
46155     /**
46156      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46157      */
46158     activate : function(){
46159         this.tabPanel.activate(this.id);
46160     },
46161
46162     /**
46163      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46164      */
46165     disable : function(){
46166         if(this.tabPanel.active != this){
46167             this.disabled = true;
46168             this.status_node.addClass("disabled");
46169         }
46170     },
46171
46172     /**
46173      * Enables this TabPanelItem if it was previously disabled.
46174      */
46175     enable : function(){
46176         this.disabled = false;
46177         this.status_node.removeClass("disabled");
46178     },
46179
46180     /**
46181      * Sets the content for this TabPanelItem.
46182      * @param {String} content The content
46183      * @param {Boolean} loadScripts true to look for and load scripts
46184      */
46185     setContent : function(content, loadScripts){
46186         this.bodyEl.update(content, loadScripts);
46187     },
46188
46189     /**
46190      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
46191      * @return {Roo.UpdateManager} The UpdateManager
46192      */
46193     getUpdateManager : function(){
46194         return this.bodyEl.getUpdateManager();
46195     },
46196
46197     /**
46198      * Set a URL to be used to load the content for this TabPanelItem.
46199      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
46200      * @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)
46201      * @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)
46202      * @return {Roo.UpdateManager} The UpdateManager
46203      */
46204     setUrl : function(url, params, loadOnce){
46205         if(this.refreshDelegate){
46206             this.un('activate', this.refreshDelegate);
46207         }
46208         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46209         this.on("activate", this.refreshDelegate);
46210         return this.bodyEl.getUpdateManager();
46211     },
46212
46213     /** @private */
46214     _handleRefresh : function(url, params, loadOnce){
46215         if(!loadOnce || !this.loaded){
46216             var updater = this.bodyEl.getUpdateManager();
46217             updater.update(url, params, this._setLoaded.createDelegate(this));
46218         }
46219     },
46220
46221     /**
46222      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
46223      *   Will fail silently if the setUrl method has not been called.
46224      *   This does not activate the panel, just updates its content.
46225      */
46226     refresh : function(){
46227         if(this.refreshDelegate){
46228            this.loaded = false;
46229            this.refreshDelegate();
46230         }
46231     },
46232
46233     /** @private */
46234     _setLoaded : function(){
46235         this.loaded = true;
46236     },
46237
46238     /** @private */
46239     closeClick : function(e){
46240         var o = {};
46241         e.stopEvent();
46242         this.fireEvent("beforeclose", this, o);
46243         if(o.cancel !== true){
46244             this.tabPanel.removeTab(this.id);
46245         }
46246     },
46247     /**
46248      * The text displayed in the tooltip for the close icon.
46249      * @type String
46250      */
46251     closeText : "Close this tab"
46252 });
46253 /**
46254 *    This script refer to:
46255 *    Title: International Telephone Input
46256 *    Author: Jack O'Connor
46257 *    Code version:  v12.1.12
46258 *    Availability: https://github.com/jackocnr/intl-tel-input.git
46259 **/
46260
46261 Roo.bootstrap.form.PhoneInputData = function() {
46262     var d = [
46263       [
46264         "Afghanistan (‫افغانستان‬‎)",
46265         "af",
46266         "93"
46267       ],
46268       [
46269         "Albania (Shqipëri)",
46270         "al",
46271         "355"
46272       ],
46273       [
46274         "Algeria (‫الجزائر‬‎)",
46275         "dz",
46276         "213"
46277       ],
46278       [
46279         "American Samoa",
46280         "as",
46281         "1684"
46282       ],
46283       [
46284         "Andorra",
46285         "ad",
46286         "376"
46287       ],
46288       [
46289         "Angola",
46290         "ao",
46291         "244"
46292       ],
46293       [
46294         "Anguilla",
46295         "ai",
46296         "1264"
46297       ],
46298       [
46299         "Antigua and Barbuda",
46300         "ag",
46301         "1268"
46302       ],
46303       [
46304         "Argentina",
46305         "ar",
46306         "54"
46307       ],
46308       [
46309         "Armenia (Հայաստան)",
46310         "am",
46311         "374"
46312       ],
46313       [
46314         "Aruba",
46315         "aw",
46316         "297"
46317       ],
46318       [
46319         "Australia",
46320         "au",
46321         "61",
46322         0
46323       ],
46324       [
46325         "Austria (Österreich)",
46326         "at",
46327         "43"
46328       ],
46329       [
46330         "Azerbaijan (Azərbaycan)",
46331         "az",
46332         "994"
46333       ],
46334       [
46335         "Bahamas",
46336         "bs",
46337         "1242"
46338       ],
46339       [
46340         "Bahrain (‫البحرين‬‎)",
46341         "bh",
46342         "973"
46343       ],
46344       [
46345         "Bangladesh (বাংলাদেশ)",
46346         "bd",
46347         "880"
46348       ],
46349       [
46350         "Barbados",
46351         "bb",
46352         "1246"
46353       ],
46354       [
46355         "Belarus (Беларусь)",
46356         "by",
46357         "375"
46358       ],
46359       [
46360         "Belgium (België)",
46361         "be",
46362         "32"
46363       ],
46364       [
46365         "Belize",
46366         "bz",
46367         "501"
46368       ],
46369       [
46370         "Benin (Bénin)",
46371         "bj",
46372         "229"
46373       ],
46374       [
46375         "Bermuda",
46376         "bm",
46377         "1441"
46378       ],
46379       [
46380         "Bhutan (འབྲུག)",
46381         "bt",
46382         "975"
46383       ],
46384       [
46385         "Bolivia",
46386         "bo",
46387         "591"
46388       ],
46389       [
46390         "Bosnia and Herzegovina (Босна и Херцеговина)",
46391         "ba",
46392         "387"
46393       ],
46394       [
46395         "Botswana",
46396         "bw",
46397         "267"
46398       ],
46399       [
46400         "Brazil (Brasil)",
46401         "br",
46402         "55"
46403       ],
46404       [
46405         "British Indian Ocean Territory",
46406         "io",
46407         "246"
46408       ],
46409       [
46410         "British Virgin Islands",
46411         "vg",
46412         "1284"
46413       ],
46414       [
46415         "Brunei",
46416         "bn",
46417         "673"
46418       ],
46419       [
46420         "Bulgaria (България)",
46421         "bg",
46422         "359"
46423       ],
46424       [
46425         "Burkina Faso",
46426         "bf",
46427         "226"
46428       ],
46429       [
46430         "Burundi (Uburundi)",
46431         "bi",
46432         "257"
46433       ],
46434       [
46435         "Cambodia (កម្ពុជា)",
46436         "kh",
46437         "855"
46438       ],
46439       [
46440         "Cameroon (Cameroun)",
46441         "cm",
46442         "237"
46443       ],
46444       [
46445         "Canada",
46446         "ca",
46447         "1",
46448         1,
46449         ["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"]
46450       ],
46451       [
46452         "Cape Verde (Kabu Verdi)",
46453         "cv",
46454         "238"
46455       ],
46456       [
46457         "Caribbean Netherlands",
46458         "bq",
46459         "599",
46460         1
46461       ],
46462       [
46463         "Cayman Islands",
46464         "ky",
46465         "1345"
46466       ],
46467       [
46468         "Central African Republic (République centrafricaine)",
46469         "cf",
46470         "236"
46471       ],
46472       [
46473         "Chad (Tchad)",
46474         "td",
46475         "235"
46476       ],
46477       [
46478         "Chile",
46479         "cl",
46480         "56"
46481       ],
46482       [
46483         "China (中国)",
46484         "cn",
46485         "86"
46486       ],
46487       [
46488         "Christmas Island",
46489         "cx",
46490         "61",
46491         2
46492       ],
46493       [
46494         "Cocos (Keeling) Islands",
46495         "cc",
46496         "61",
46497         1
46498       ],
46499       [
46500         "Colombia",
46501         "co",
46502         "57"
46503       ],
46504       [
46505         "Comoros (‫جزر القمر‬‎)",
46506         "km",
46507         "269"
46508       ],
46509       [
46510         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
46511         "cd",
46512         "243"
46513       ],
46514       [
46515         "Congo (Republic) (Congo-Brazzaville)",
46516         "cg",
46517         "242"
46518       ],
46519       [
46520         "Cook Islands",
46521         "ck",
46522         "682"
46523       ],
46524       [
46525         "Costa Rica",
46526         "cr",
46527         "506"
46528       ],
46529       [
46530         "Côte d’Ivoire",
46531         "ci",
46532         "225"
46533       ],
46534       [
46535         "Croatia (Hrvatska)",
46536         "hr",
46537         "385"
46538       ],
46539       [
46540         "Cuba",
46541         "cu",
46542         "53"
46543       ],
46544       [
46545         "Curaçao",
46546         "cw",
46547         "599",
46548         0
46549       ],
46550       [
46551         "Cyprus (Κύπρος)",
46552         "cy",
46553         "357"
46554       ],
46555       [
46556         "Czech Republic (Česká republika)",
46557         "cz",
46558         "420"
46559       ],
46560       [
46561         "Denmark (Danmark)",
46562         "dk",
46563         "45"
46564       ],
46565       [
46566         "Djibouti",
46567         "dj",
46568         "253"
46569       ],
46570       [
46571         "Dominica",
46572         "dm",
46573         "1767"
46574       ],
46575       [
46576         "Dominican Republic (República Dominicana)",
46577         "do",
46578         "1",
46579         2,
46580         ["809", "829", "849"]
46581       ],
46582       [
46583         "Ecuador",
46584         "ec",
46585         "593"
46586       ],
46587       [
46588         "Egypt (‫مصر‬‎)",
46589         "eg",
46590         "20"
46591       ],
46592       [
46593         "El Salvador",
46594         "sv",
46595         "503"
46596       ],
46597       [
46598         "Equatorial Guinea (Guinea Ecuatorial)",
46599         "gq",
46600         "240"
46601       ],
46602       [
46603         "Eritrea",
46604         "er",
46605         "291"
46606       ],
46607       [
46608         "Estonia (Eesti)",
46609         "ee",
46610         "372"
46611       ],
46612       [
46613         "Ethiopia",
46614         "et",
46615         "251"
46616       ],
46617       [
46618         "Falkland Islands (Islas Malvinas)",
46619         "fk",
46620         "500"
46621       ],
46622       [
46623         "Faroe Islands (Føroyar)",
46624         "fo",
46625         "298"
46626       ],
46627       [
46628         "Fiji",
46629         "fj",
46630         "679"
46631       ],
46632       [
46633         "Finland (Suomi)",
46634         "fi",
46635         "358",
46636         0
46637       ],
46638       [
46639         "France",
46640         "fr",
46641         "33"
46642       ],
46643       [
46644         "French Guiana (Guyane française)",
46645         "gf",
46646         "594"
46647       ],
46648       [
46649         "French Polynesia (Polynésie française)",
46650         "pf",
46651         "689"
46652       ],
46653       [
46654         "Gabon",
46655         "ga",
46656         "241"
46657       ],
46658       [
46659         "Gambia",
46660         "gm",
46661         "220"
46662       ],
46663       [
46664         "Georgia (საქართველო)",
46665         "ge",
46666         "995"
46667       ],
46668       [
46669         "Germany (Deutschland)",
46670         "de",
46671         "49"
46672       ],
46673       [
46674         "Ghana (Gaana)",
46675         "gh",
46676         "233"
46677       ],
46678       [
46679         "Gibraltar",
46680         "gi",
46681         "350"
46682       ],
46683       [
46684         "Greece (Ελλάδα)",
46685         "gr",
46686         "30"
46687       ],
46688       [
46689         "Greenland (Kalaallit Nunaat)",
46690         "gl",
46691         "299"
46692       ],
46693       [
46694         "Grenada",
46695         "gd",
46696         "1473"
46697       ],
46698       [
46699         "Guadeloupe",
46700         "gp",
46701         "590",
46702         0
46703       ],
46704       [
46705         "Guam",
46706         "gu",
46707         "1671"
46708       ],
46709       [
46710         "Guatemala",
46711         "gt",
46712         "502"
46713       ],
46714       [
46715         "Guernsey",
46716         "gg",
46717         "44",
46718         1
46719       ],
46720       [
46721         "Guinea (Guinée)",
46722         "gn",
46723         "224"
46724       ],
46725       [
46726         "Guinea-Bissau (Guiné Bissau)",
46727         "gw",
46728         "245"
46729       ],
46730       [
46731         "Guyana",
46732         "gy",
46733         "592"
46734       ],
46735       [
46736         "Haiti",
46737         "ht",
46738         "509"
46739       ],
46740       [
46741         "Honduras",
46742         "hn",
46743         "504"
46744       ],
46745       [
46746         "Hong Kong (香港)",
46747         "hk",
46748         "852"
46749       ],
46750       [
46751         "Hungary (Magyarország)",
46752         "hu",
46753         "36"
46754       ],
46755       [
46756         "Iceland (Ísland)",
46757         "is",
46758         "354"
46759       ],
46760       [
46761         "India (भारत)",
46762         "in",
46763         "91"
46764       ],
46765       [
46766         "Indonesia",
46767         "id",
46768         "62"
46769       ],
46770       [
46771         "Iran (‫ایران‬‎)",
46772         "ir",
46773         "98"
46774       ],
46775       [
46776         "Iraq (‫العراق‬‎)",
46777         "iq",
46778         "964"
46779       ],
46780       [
46781         "Ireland",
46782         "ie",
46783         "353"
46784       ],
46785       [
46786         "Isle of Man",
46787         "im",
46788         "44",
46789         2
46790       ],
46791       [
46792         "Israel (‫ישראל‬‎)",
46793         "il",
46794         "972"
46795       ],
46796       [
46797         "Italy (Italia)",
46798         "it",
46799         "39",
46800         0
46801       ],
46802       [
46803         "Jamaica",
46804         "jm",
46805         "1876"
46806       ],
46807       [
46808         "Japan (日本)",
46809         "jp",
46810         "81"
46811       ],
46812       [
46813         "Jersey",
46814         "je",
46815         "44",
46816         3
46817       ],
46818       [
46819         "Jordan (‫الأردن‬‎)",
46820         "jo",
46821         "962"
46822       ],
46823       [
46824         "Kazakhstan (Казахстан)",
46825         "kz",
46826         "7",
46827         1
46828       ],
46829       [
46830         "Kenya",
46831         "ke",
46832         "254"
46833       ],
46834       [
46835         "Kiribati",
46836         "ki",
46837         "686"
46838       ],
46839       [
46840         "Kosovo",
46841         "xk",
46842         "383"
46843       ],
46844       [
46845         "Kuwait (‫الكويت‬‎)",
46846         "kw",
46847         "965"
46848       ],
46849       [
46850         "Kyrgyzstan (Кыргызстан)",
46851         "kg",
46852         "996"
46853       ],
46854       [
46855         "Laos (ລາວ)",
46856         "la",
46857         "856"
46858       ],
46859       [
46860         "Latvia (Latvija)",
46861         "lv",
46862         "371"
46863       ],
46864       [
46865         "Lebanon (‫لبنان‬‎)",
46866         "lb",
46867         "961"
46868       ],
46869       [
46870         "Lesotho",
46871         "ls",
46872         "266"
46873       ],
46874       [
46875         "Liberia",
46876         "lr",
46877         "231"
46878       ],
46879       [
46880         "Libya (‫ليبيا‬‎)",
46881         "ly",
46882         "218"
46883       ],
46884       [
46885         "Liechtenstein",
46886         "li",
46887         "423"
46888       ],
46889       [
46890         "Lithuania (Lietuva)",
46891         "lt",
46892         "370"
46893       ],
46894       [
46895         "Luxembourg",
46896         "lu",
46897         "352"
46898       ],
46899       [
46900         "Macau (澳門)",
46901         "mo",
46902         "853"
46903       ],
46904       [
46905         "Macedonia (FYROM) (Македонија)",
46906         "mk",
46907         "389"
46908       ],
46909       [
46910         "Madagascar (Madagasikara)",
46911         "mg",
46912         "261"
46913       ],
46914       [
46915         "Malawi",
46916         "mw",
46917         "265"
46918       ],
46919       [
46920         "Malaysia",
46921         "my",
46922         "60"
46923       ],
46924       [
46925         "Maldives",
46926         "mv",
46927         "960"
46928       ],
46929       [
46930         "Mali",
46931         "ml",
46932         "223"
46933       ],
46934       [
46935         "Malta",
46936         "mt",
46937         "356"
46938       ],
46939       [
46940         "Marshall Islands",
46941         "mh",
46942         "692"
46943       ],
46944       [
46945         "Martinique",
46946         "mq",
46947         "596"
46948       ],
46949       [
46950         "Mauritania (‫موريتانيا‬‎)",
46951         "mr",
46952         "222"
46953       ],
46954       [
46955         "Mauritius (Moris)",
46956         "mu",
46957         "230"
46958       ],
46959       [
46960         "Mayotte",
46961         "yt",
46962         "262",
46963         1
46964       ],
46965       [
46966         "Mexico (México)",
46967         "mx",
46968         "52"
46969       ],
46970       [
46971         "Micronesia",
46972         "fm",
46973         "691"
46974       ],
46975       [
46976         "Moldova (Republica Moldova)",
46977         "md",
46978         "373"
46979       ],
46980       [
46981         "Monaco",
46982         "mc",
46983         "377"
46984       ],
46985       [
46986         "Mongolia (Монгол)",
46987         "mn",
46988         "976"
46989       ],
46990       [
46991         "Montenegro (Crna Gora)",
46992         "me",
46993         "382"
46994       ],
46995       [
46996         "Montserrat",
46997         "ms",
46998         "1664"
46999       ],
47000       [
47001         "Morocco (‫المغرب‬‎)",
47002         "ma",
47003         "212",
47004         0
47005       ],
47006       [
47007         "Mozambique (Moçambique)",
47008         "mz",
47009         "258"
47010       ],
47011       [
47012         "Myanmar (Burma) (မြန်မာ)",
47013         "mm",
47014         "95"
47015       ],
47016       [
47017         "Namibia (Namibië)",
47018         "na",
47019         "264"
47020       ],
47021       [
47022         "Nauru",
47023         "nr",
47024         "674"
47025       ],
47026       [
47027         "Nepal (नेपाल)",
47028         "np",
47029         "977"
47030       ],
47031       [
47032         "Netherlands (Nederland)",
47033         "nl",
47034         "31"
47035       ],
47036       [
47037         "New Caledonia (Nouvelle-Calédonie)",
47038         "nc",
47039         "687"
47040       ],
47041       [
47042         "New Zealand",
47043         "nz",
47044         "64"
47045       ],
47046       [
47047         "Nicaragua",
47048         "ni",
47049         "505"
47050       ],
47051       [
47052         "Niger (Nijar)",
47053         "ne",
47054         "227"
47055       ],
47056       [
47057         "Nigeria",
47058         "ng",
47059         "234"
47060       ],
47061       [
47062         "Niue",
47063         "nu",
47064         "683"
47065       ],
47066       [
47067         "Norfolk Island",
47068         "nf",
47069         "672"
47070       ],
47071       [
47072         "North Korea (조선 민주주의 인민 공화국)",
47073         "kp",
47074         "850"
47075       ],
47076       [
47077         "Northern Mariana Islands",
47078         "mp",
47079         "1670"
47080       ],
47081       [
47082         "Norway (Norge)",
47083         "no",
47084         "47",
47085         0
47086       ],
47087       [
47088         "Oman (‫عُمان‬‎)",
47089         "om",
47090         "968"
47091       ],
47092       [
47093         "Pakistan (‫پاکستان‬‎)",
47094         "pk",
47095         "92"
47096       ],
47097       [
47098         "Palau",
47099         "pw",
47100         "680"
47101       ],
47102       [
47103         "Palestine (‫فلسطين‬‎)",
47104         "ps",
47105         "970"
47106       ],
47107       [
47108         "Panama (Panamá)",
47109         "pa",
47110         "507"
47111       ],
47112       [
47113         "Papua New Guinea",
47114         "pg",
47115         "675"
47116       ],
47117       [
47118         "Paraguay",
47119         "py",
47120         "595"
47121       ],
47122       [
47123         "Peru (Perú)",
47124         "pe",
47125         "51"
47126       ],
47127       [
47128         "Philippines",
47129         "ph",
47130         "63"
47131       ],
47132       [
47133         "Poland (Polska)",
47134         "pl",
47135         "48"
47136       ],
47137       [
47138         "Portugal",
47139         "pt",
47140         "351"
47141       ],
47142       [
47143         "Puerto Rico",
47144         "pr",
47145         "1",
47146         3,
47147         ["787", "939"]
47148       ],
47149       [
47150         "Qatar (‫قطر‬‎)",
47151         "qa",
47152         "974"
47153       ],
47154       [
47155         "Réunion (La Réunion)",
47156         "re",
47157         "262",
47158         0
47159       ],
47160       [
47161         "Romania (România)",
47162         "ro",
47163         "40"
47164       ],
47165       [
47166         "Russia (Россия)",
47167         "ru",
47168         "7",
47169         0
47170       ],
47171       [
47172         "Rwanda",
47173         "rw",
47174         "250"
47175       ],
47176       [
47177         "Saint Barthélemy",
47178         "bl",
47179         "590",
47180         1
47181       ],
47182       [
47183         "Saint Helena",
47184         "sh",
47185         "290"
47186       ],
47187       [
47188         "Saint Kitts and Nevis",
47189         "kn",
47190         "1869"
47191       ],
47192       [
47193         "Saint Lucia",
47194         "lc",
47195         "1758"
47196       ],
47197       [
47198         "Saint Martin (Saint-Martin (partie française))",
47199         "mf",
47200         "590",
47201         2
47202       ],
47203       [
47204         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
47205         "pm",
47206         "508"
47207       ],
47208       [
47209         "Saint Vincent and the Grenadines",
47210         "vc",
47211         "1784"
47212       ],
47213       [
47214         "Samoa",
47215         "ws",
47216         "685"
47217       ],
47218       [
47219         "San Marino",
47220         "sm",
47221         "378"
47222       ],
47223       [
47224         "São Tomé and Príncipe (São Tomé e Príncipe)",
47225         "st",
47226         "239"
47227       ],
47228       [
47229         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
47230         "sa",
47231         "966"
47232       ],
47233       [
47234         "Senegal (Sénégal)",
47235         "sn",
47236         "221"
47237       ],
47238       [
47239         "Serbia (Србија)",
47240         "rs",
47241         "381"
47242       ],
47243       [
47244         "Seychelles",
47245         "sc",
47246         "248"
47247       ],
47248       [
47249         "Sierra Leone",
47250         "sl",
47251         "232"
47252       ],
47253       [
47254         "Singapore",
47255         "sg",
47256         "65"
47257       ],
47258       [
47259         "Sint Maarten",
47260         "sx",
47261         "1721"
47262       ],
47263       [
47264         "Slovakia (Slovensko)",
47265         "sk",
47266         "421"
47267       ],
47268       [
47269         "Slovenia (Slovenija)",
47270         "si",
47271         "386"
47272       ],
47273       [
47274         "Solomon Islands",
47275         "sb",
47276         "677"
47277       ],
47278       [
47279         "Somalia (Soomaaliya)",
47280         "so",
47281         "252"
47282       ],
47283       [
47284         "South Africa",
47285         "za",
47286         "27"
47287       ],
47288       [
47289         "South Korea (대한민국)",
47290         "kr",
47291         "82"
47292       ],
47293       [
47294         "South Sudan (‫جنوب السودان‬‎)",
47295         "ss",
47296         "211"
47297       ],
47298       [
47299         "Spain (España)",
47300         "es",
47301         "34"
47302       ],
47303       [
47304         "Sri Lanka (ශ්‍රී ලංකාව)",
47305         "lk",
47306         "94"
47307       ],
47308       [
47309         "Sudan (‫السودان‬‎)",
47310         "sd",
47311         "249"
47312       ],
47313       [
47314         "Suriname",
47315         "sr",
47316         "597"
47317       ],
47318       [
47319         "Svalbard and Jan Mayen",
47320         "sj",
47321         "47",
47322         1
47323       ],
47324       [
47325         "Swaziland",
47326         "sz",
47327         "268"
47328       ],
47329       [
47330         "Sweden (Sverige)",
47331         "se",
47332         "46"
47333       ],
47334       [
47335         "Switzerland (Schweiz)",
47336         "ch",
47337         "41"
47338       ],
47339       [
47340         "Syria (‫سوريا‬‎)",
47341         "sy",
47342         "963"
47343       ],
47344       [
47345         "Taiwan (台灣)",
47346         "tw",
47347         "886"
47348       ],
47349       [
47350         "Tajikistan",
47351         "tj",
47352         "992"
47353       ],
47354       [
47355         "Tanzania",
47356         "tz",
47357         "255"
47358       ],
47359       [
47360         "Thailand (ไทย)",
47361         "th",
47362         "66"
47363       ],
47364       [
47365         "Timor-Leste",
47366         "tl",
47367         "670"
47368       ],
47369       [
47370         "Togo",
47371         "tg",
47372         "228"
47373       ],
47374       [
47375         "Tokelau",
47376         "tk",
47377         "690"
47378       ],
47379       [
47380         "Tonga",
47381         "to",
47382         "676"
47383       ],
47384       [
47385         "Trinidad and Tobago",
47386         "tt",
47387         "1868"
47388       ],
47389       [
47390         "Tunisia (‫تونس‬‎)",
47391         "tn",
47392         "216"
47393       ],
47394       [
47395         "Turkey (Türkiye)",
47396         "tr",
47397         "90"
47398       ],
47399       [
47400         "Turkmenistan",
47401         "tm",
47402         "993"
47403       ],
47404       [
47405         "Turks and Caicos Islands",
47406         "tc",
47407         "1649"
47408       ],
47409       [
47410         "Tuvalu",
47411         "tv",
47412         "688"
47413       ],
47414       [
47415         "U.S. Virgin Islands",
47416         "vi",
47417         "1340"
47418       ],
47419       [
47420         "Uganda",
47421         "ug",
47422         "256"
47423       ],
47424       [
47425         "Ukraine (Україна)",
47426         "ua",
47427         "380"
47428       ],
47429       [
47430         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
47431         "ae",
47432         "971"
47433       ],
47434       [
47435         "United Kingdom",
47436         "gb",
47437         "44",
47438         0
47439       ],
47440       [
47441         "United States",
47442         "us",
47443         "1",
47444         0
47445       ],
47446       [
47447         "Uruguay",
47448         "uy",
47449         "598"
47450       ],
47451       [
47452         "Uzbekistan (Oʻzbekiston)",
47453         "uz",
47454         "998"
47455       ],
47456       [
47457         "Vanuatu",
47458         "vu",
47459         "678"
47460       ],
47461       [
47462         "Vatican City (Città del Vaticano)",
47463         "va",
47464         "39",
47465         1
47466       ],
47467       [
47468         "Venezuela",
47469         "ve",
47470         "58"
47471       ],
47472       [
47473         "Vietnam (Việt Nam)",
47474         "vn",
47475         "84"
47476       ],
47477       [
47478         "Wallis and Futuna (Wallis-et-Futuna)",
47479         "wf",
47480         "681"
47481       ],
47482       [
47483         "Western Sahara (‫الصحراء الغربية‬‎)",
47484         "eh",
47485         "212",
47486         1
47487       ],
47488       [
47489         "Yemen (‫اليمن‬‎)",
47490         "ye",
47491         "967"
47492       ],
47493       [
47494         "Zambia",
47495         "zm",
47496         "260"
47497       ],
47498       [
47499         "Zimbabwe",
47500         "zw",
47501         "263"
47502       ],
47503       [
47504         "Åland Islands",
47505         "ax",
47506         "358",
47507         1
47508       ]
47509   ];
47510   
47511   return d;
47512 }/**
47513 *    This script refer to:
47514 *    Title: International Telephone Input
47515 *    Author: Jack O'Connor
47516 *    Code version:  v12.1.12
47517 *    Availability: https://github.com/jackocnr/intl-tel-input.git
47518 **/
47519
47520 /**
47521  * @class Roo.bootstrap.form.PhoneInput
47522  * @extends Roo.bootstrap.form.TriggerField
47523  * An input with International dial-code selection
47524  
47525  * @cfg {String} defaultDialCode default '+852'
47526  * @cfg {Array} preferedCountries default []
47527   
47528  * @constructor
47529  * Create a new PhoneInput.
47530  * @param {Object} config Configuration options
47531  */
47532
47533 Roo.bootstrap.form.PhoneInput = function(config) {
47534     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
47535 };
47536
47537 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
47538         /**
47539         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
47540         */
47541         listWidth: undefined,
47542         
47543         selectedClass: 'active',
47544         
47545         invalidClass : "has-warning",
47546         
47547         validClass: 'has-success',
47548         
47549         allowed: '0123456789',
47550         
47551         max_length: 15,
47552         
47553         /**
47554          * @cfg {String} defaultDialCode The default dial code when initializing the input
47555          */
47556         defaultDialCode: '+852',
47557         
47558         /**
47559          * @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
47560          */
47561         preferedCountries: false,
47562         
47563         getAutoCreate : function()
47564         {
47565             var data = Roo.bootstrap.form.PhoneInputData();
47566             var align = this.labelAlign || this.parentLabelAlign();
47567             var id = Roo.id();
47568             
47569             this.allCountries = [];
47570             this.dialCodeMapping = [];
47571             
47572             for (var i = 0; i < data.length; i++) {
47573               var c = data[i];
47574               this.allCountries[i] = {
47575                 name: c[0],
47576                 iso2: c[1],
47577                 dialCode: c[2],
47578                 priority: c[3] || 0,
47579                 areaCodes: c[4] || null
47580               };
47581               this.dialCodeMapping[c[2]] = {
47582                   name: c[0],
47583                   iso2: c[1],
47584                   priority: c[3] || 0,
47585                   areaCodes: c[4] || null
47586               };
47587             }
47588             
47589             var cfg = {
47590                 cls: 'form-group',
47591                 cn: []
47592             };
47593             
47594             var input =  {
47595                 tag: 'input',
47596                 id : id,
47597                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
47598                 maxlength: this.max_length,
47599                 cls : 'form-control tel-input',
47600                 autocomplete: 'new-password'
47601             };
47602             
47603             var hiddenInput = {
47604                 tag: 'input',
47605                 type: 'hidden',
47606                 cls: 'hidden-tel-input'
47607             };
47608             
47609             if (this.name) {
47610                 hiddenInput.name = this.name;
47611             }
47612             
47613             if (this.disabled) {
47614                 input.disabled = true;
47615             }
47616             
47617             var flag_container = {
47618                 tag: 'div',
47619                 cls: 'flag-box',
47620                 cn: [
47621                     {
47622                         tag: 'div',
47623                         cls: 'flag'
47624                     },
47625                     {
47626                         tag: 'div',
47627                         cls: 'caret'
47628                     }
47629                 ]
47630             };
47631             
47632             var box = {
47633                 tag: 'div',
47634                 cls: this.hasFeedback ? 'has-feedback' : '',
47635                 cn: [
47636                     hiddenInput,
47637                     input,
47638                     {
47639                         tag: 'input',
47640                         cls: 'dial-code-holder',
47641                         disabled: true
47642                     }
47643                 ]
47644             };
47645             
47646             var container = {
47647                 cls: 'roo-select2-container input-group',
47648                 cn: [
47649                     flag_container,
47650                     box
47651                 ]
47652             };
47653             
47654             if (this.fieldLabel.length) {
47655                 var indicator = {
47656                     tag: 'i',
47657                     tooltip: 'This field is required'
47658                 };
47659                 
47660                 var label = {
47661                     tag: 'label',
47662                     'for':  id,
47663                     cls: 'control-label',
47664                     cn: []
47665                 };
47666                 
47667                 var label_text = {
47668                     tag: 'span',
47669                     html: this.fieldLabel
47670                 };
47671                 
47672                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
47673                 label.cn = [
47674                     indicator,
47675                     label_text
47676                 ];
47677                 
47678                 if(this.indicatorpos == 'right') {
47679                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
47680                     label.cn = [
47681                         label_text,
47682                         indicator
47683                     ];
47684                 }
47685                 
47686                 if(align == 'left') {
47687                     container = {
47688                         tag: 'div',
47689                         cn: [
47690                             container
47691                         ]
47692                     };
47693                     
47694                     if(this.labelWidth > 12){
47695                         label.style = "width: " + this.labelWidth + 'px';
47696                     }
47697                     if(this.labelWidth < 13 && this.labelmd == 0){
47698                         this.labelmd = this.labelWidth;
47699                     }
47700                     if(this.labellg > 0){
47701                         label.cls += ' col-lg-' + this.labellg;
47702                         input.cls += ' col-lg-' + (12 - this.labellg);
47703                     }
47704                     if(this.labelmd > 0){
47705                         label.cls += ' col-md-' + this.labelmd;
47706                         container.cls += ' col-md-' + (12 - this.labelmd);
47707                     }
47708                     if(this.labelsm > 0){
47709                         label.cls += ' col-sm-' + this.labelsm;
47710                         container.cls += ' col-sm-' + (12 - this.labelsm);
47711                     }
47712                     if(this.labelxs > 0){
47713                         label.cls += ' col-xs-' + this.labelxs;
47714                         container.cls += ' col-xs-' + (12 - this.labelxs);
47715                     }
47716                 }
47717             }
47718             
47719             cfg.cn = [
47720                 label,
47721                 container
47722             ];
47723             
47724             var settings = this;
47725             
47726             ['xs','sm','md','lg'].map(function(size){
47727                 if (settings[size]) {
47728                     cfg.cls += ' col-' + size + '-' + settings[size];
47729                 }
47730             });
47731             
47732             this.store = new Roo.data.Store({
47733                 proxy : new Roo.data.MemoryProxy({}),
47734                 reader : new Roo.data.JsonReader({
47735                     fields : [
47736                         {
47737                             'name' : 'name',
47738                             'type' : 'string'
47739                         },
47740                         {
47741                             'name' : 'iso2',
47742                             'type' : 'string'
47743                         },
47744                         {
47745                             'name' : 'dialCode',
47746                             'type' : 'string'
47747                         },
47748                         {
47749                             'name' : 'priority',
47750                             'type' : 'string'
47751                         },
47752                         {
47753                             'name' : 'areaCodes',
47754                             'type' : 'string'
47755                         }
47756                     ]
47757                 })
47758             });
47759             
47760             if(!this.preferedCountries) {
47761                 this.preferedCountries = [
47762                     'hk',
47763                     'gb',
47764                     'us'
47765                 ];
47766             }
47767             
47768             var p = this.preferedCountries.reverse();
47769             
47770             if(p) {
47771                 for (var i = 0; i < p.length; i++) {
47772                     for (var j = 0; j < this.allCountries.length; j++) {
47773                         if(this.allCountries[j].iso2 == p[i]) {
47774                             var t = this.allCountries[j];
47775                             this.allCountries.splice(j,1);
47776                             this.allCountries.unshift(t);
47777                         }
47778                     } 
47779                 }
47780             }
47781             
47782             this.store.proxy.data = {
47783                 success: true,
47784                 data: this.allCountries
47785             };
47786             
47787             return cfg;
47788         },
47789         
47790         initEvents : function()
47791         {
47792             this.createList();
47793             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
47794             
47795             this.indicator = this.indicatorEl();
47796             this.flag = this.flagEl();
47797             this.dialCodeHolder = this.dialCodeHolderEl();
47798             
47799             this.trigger = this.el.select('div.flag-box',true).first();
47800             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
47801             
47802             var _this = this;
47803             
47804             (function(){
47805                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
47806                 _this.list.setWidth(lw);
47807             }).defer(100);
47808             
47809             this.list.on('mouseover', this.onViewOver, this);
47810             this.list.on('mousemove', this.onViewMove, this);
47811             this.inputEl().on("keyup", this.onKeyUp, this);
47812             this.inputEl().on("keypress", this.onKeyPress, this);
47813             
47814             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
47815
47816             this.view = new Roo.View(this.list, this.tpl, {
47817                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
47818             });
47819             
47820             this.view.on('click', this.onViewClick, this);
47821             this.setValue(this.defaultDialCode);
47822         },
47823         
47824         onTriggerClick : function(e)
47825         {
47826             Roo.log('trigger click');
47827             if(this.disabled){
47828                 return;
47829             }
47830             
47831             if(this.isExpanded()){
47832                 this.collapse();
47833                 this.hasFocus = false;
47834             }else {
47835                 this.store.load({});
47836                 this.hasFocus = true;
47837                 this.expand();
47838             }
47839         },
47840         
47841         isExpanded : function()
47842         {
47843             return this.list.isVisible();
47844         },
47845         
47846         collapse : function()
47847         {
47848             if(!this.isExpanded()){
47849                 return;
47850             }
47851             this.list.hide();
47852             Roo.get(document).un('mousedown', this.collapseIf, this);
47853             Roo.get(document).un('mousewheel', this.collapseIf, this);
47854             this.fireEvent('collapse', this);
47855             this.validate();
47856         },
47857         
47858         expand : function()
47859         {
47860             Roo.log('expand');
47861
47862             if(this.isExpanded() || !this.hasFocus){
47863                 return;
47864             }
47865             
47866             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
47867             this.list.setWidth(lw);
47868             
47869             this.list.show();
47870             this.restrictHeight();
47871             
47872             Roo.get(document).on('mousedown', this.collapseIf, this);
47873             Roo.get(document).on('mousewheel', this.collapseIf, this);
47874             
47875             this.fireEvent('expand', this);
47876         },
47877         
47878         restrictHeight : function()
47879         {
47880             this.list.alignTo(this.inputEl(), this.listAlign);
47881             this.list.alignTo(this.inputEl(), this.listAlign);
47882         },
47883         
47884         onViewOver : function(e, t)
47885         {
47886             if(this.inKeyMode){
47887                 return;
47888             }
47889             var item = this.view.findItemFromChild(t);
47890             
47891             if(item){
47892                 var index = this.view.indexOf(item);
47893                 this.select(index, false);
47894             }
47895         },
47896
47897         // private
47898         onViewClick : function(view, doFocus, el, e)
47899         {
47900             var index = this.view.getSelectedIndexes()[0];
47901             
47902             var r = this.store.getAt(index);
47903             
47904             if(r){
47905                 this.onSelect(r, index);
47906             }
47907             if(doFocus !== false && !this.blockFocus){
47908                 this.inputEl().focus();
47909             }
47910         },
47911         
47912         onViewMove : function(e, t)
47913         {
47914             this.inKeyMode = false;
47915         },
47916         
47917         select : function(index, scrollIntoView)
47918         {
47919             this.selectedIndex = index;
47920             this.view.select(index);
47921             if(scrollIntoView !== false){
47922                 var el = this.view.getNode(index);
47923                 if(el){
47924                     this.list.scrollChildIntoView(el, false);
47925                 }
47926             }
47927         },
47928         
47929         createList : function()
47930         {
47931             this.list = Roo.get(document.body).createChild({
47932                 tag: 'ul',
47933                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
47934                 style: 'display:none'
47935             });
47936             
47937             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
47938         },
47939         
47940         collapseIf : function(e)
47941         {
47942             var in_combo  = e.within(this.el);
47943             var in_list =  e.within(this.list);
47944             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
47945             
47946             if (in_combo || in_list || is_list) {
47947                 return;
47948             }
47949             this.collapse();
47950         },
47951         
47952         onSelect : function(record, index)
47953         {
47954             if(this.fireEvent('beforeselect', this, record, index) !== false){
47955                 
47956                 this.setFlagClass(record.data.iso2);
47957                 this.setDialCode(record.data.dialCode);
47958                 this.hasFocus = false;
47959                 this.collapse();
47960                 this.fireEvent('select', this, record, index);
47961             }
47962         },
47963         
47964         flagEl : function()
47965         {
47966             var flag = this.el.select('div.flag',true).first();
47967             if(!flag){
47968                 return false;
47969             }
47970             return flag;
47971         },
47972         
47973         dialCodeHolderEl : function()
47974         {
47975             var d = this.el.select('input.dial-code-holder',true).first();
47976             if(!d){
47977                 return false;
47978             }
47979             return d;
47980         },
47981         
47982         setDialCode : function(v)
47983         {
47984             this.dialCodeHolder.dom.value = '+'+v;
47985         },
47986         
47987         setFlagClass : function(n)
47988         {
47989             this.flag.dom.className = 'flag '+n;
47990         },
47991         
47992         getValue : function()
47993         {
47994             var v = this.inputEl().getValue();
47995             if(this.dialCodeHolder) {
47996                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
47997             }
47998             return v;
47999         },
48000         
48001         setValue : function(v)
48002         {
48003             var d = this.getDialCode(v);
48004             
48005             //invalid dial code
48006             if(v.length == 0 || !d || d.length == 0) {
48007                 if(this.rendered){
48008                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48009                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48010                 }
48011                 return;
48012             }
48013             
48014             //valid dial code
48015             this.setFlagClass(this.dialCodeMapping[d].iso2);
48016             this.setDialCode(d);
48017             this.inputEl().dom.value = v.replace('+'+d,'');
48018             this.hiddenEl().dom.value = this.getValue();
48019             
48020             this.validate();
48021         },
48022         
48023         getDialCode : function(v)
48024         {
48025             v = v ||  '';
48026             
48027             if (v.length == 0) {
48028                 return this.dialCodeHolder.dom.value;
48029             }
48030             
48031             var dialCode = "";
48032             if (v.charAt(0) != "+") {
48033                 return false;
48034             }
48035             var numericChars = "";
48036             for (var i = 1; i < v.length; i++) {
48037               var c = v.charAt(i);
48038               if (!isNaN(c)) {
48039                 numericChars += c;
48040                 if (this.dialCodeMapping[numericChars]) {
48041                   dialCode = v.substr(1, i);
48042                 }
48043                 if (numericChars.length == 4) {
48044                   break;
48045                 }
48046               }
48047             }
48048             return dialCode;
48049         },
48050         
48051         reset : function()
48052         {
48053             this.setValue(this.defaultDialCode);
48054             this.validate();
48055         },
48056         
48057         hiddenEl : function()
48058         {
48059             return this.el.select('input.hidden-tel-input',true).first();
48060         },
48061         
48062         // after setting val
48063         onKeyUp : function(e){
48064             this.setValue(this.getValue());
48065         },
48066         
48067         onKeyPress : function(e){
48068             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48069                 e.stopEvent();
48070             }
48071         }
48072         
48073 });
48074 /**
48075  * @class Roo.bootstrap.form.MoneyField
48076  * @extends Roo.bootstrap.form.ComboBox
48077  * Bootstrap MoneyField class
48078  * 
48079  * @constructor
48080  * Create a new MoneyField.
48081  * @param {Object} config Configuration options
48082  */
48083
48084 Roo.bootstrap.form.MoneyField = function(config) {
48085     
48086     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48087     
48088 };
48089
48090 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48091     
48092     /**
48093      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48094      */
48095     allowDecimals : true,
48096     /**
48097      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48098      */
48099     decimalSeparator : ".",
48100     /**
48101      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48102      */
48103     decimalPrecision : 0,
48104     /**
48105      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48106      */
48107     allowNegative : true,
48108     /**
48109      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48110      */
48111     allowZero: true,
48112     /**
48113      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48114      */
48115     minValue : Number.NEGATIVE_INFINITY,
48116     /**
48117      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48118      */
48119     maxValue : Number.MAX_VALUE,
48120     /**
48121      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48122      */
48123     minText : "The minimum value for this field is {0}",
48124     /**
48125      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48126      */
48127     maxText : "The maximum value for this field is {0}",
48128     /**
48129      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
48130      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48131      */
48132     nanText : "{0} is not a valid number",
48133     /**
48134      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48135      */
48136     castInt : true,
48137     /**
48138      * @cfg {String} defaults currency of the MoneyField
48139      * value should be in lkey
48140      */
48141     defaultCurrency : false,
48142     /**
48143      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48144      */
48145     thousandsDelimiter : false,
48146     /**
48147      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48148      */
48149     max_length: false,
48150     
48151     inputlg : 9,
48152     inputmd : 9,
48153     inputsm : 9,
48154     inputxs : 6,
48155      /**
48156      * @cfg {Roo.data.Store} store  Store to lookup currency??
48157      */
48158     store : false,
48159     
48160     getAutoCreate : function()
48161     {
48162         var align = this.labelAlign || this.parentLabelAlign();
48163         
48164         var id = Roo.id();
48165
48166         var cfg = {
48167             cls: 'form-group',
48168             cn: []
48169         };
48170
48171         var input =  {
48172             tag: 'input',
48173             id : id,
48174             cls : 'form-control roo-money-amount-input',
48175             autocomplete: 'new-password'
48176         };
48177         
48178         var hiddenInput = {
48179             tag: 'input',
48180             type: 'hidden',
48181             id: Roo.id(),
48182             cls: 'hidden-number-input'
48183         };
48184         
48185         if(this.max_length) {
48186             input.maxlength = this.max_length; 
48187         }
48188         
48189         if (this.name) {
48190             hiddenInput.name = this.name;
48191         }
48192
48193         if (this.disabled) {
48194             input.disabled = true;
48195         }
48196
48197         var clg = 12 - this.inputlg;
48198         var cmd = 12 - this.inputmd;
48199         var csm = 12 - this.inputsm;
48200         var cxs = 12 - this.inputxs;
48201         
48202         var container = {
48203             tag : 'div',
48204             cls : 'row roo-money-field',
48205             cn : [
48206                 {
48207                     tag : 'div',
48208                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
48209                     cn : [
48210                         {
48211                             tag : 'div',
48212                             cls: 'roo-select2-container input-group',
48213                             cn: [
48214                                 {
48215                                     tag : 'input',
48216                                     cls : 'form-control roo-money-currency-input',
48217                                     autocomplete: 'new-password',
48218                                     readOnly : 1,
48219                                     name : this.currencyName
48220                                 },
48221                                 {
48222                                     tag :'span',
48223                                     cls : 'input-group-addon',
48224                                     cn : [
48225                                         {
48226                                             tag: 'span',
48227                                             cls: 'caret'
48228                                         }
48229                                     ]
48230                                 }
48231                             ]
48232                         }
48233                     ]
48234                 },
48235                 {
48236                     tag : 'div',
48237                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
48238                     cn : [
48239                         {
48240                             tag: 'div',
48241                             cls: this.hasFeedback ? 'has-feedback' : '',
48242                             cn: [
48243                                 input
48244                             ]
48245                         }
48246                     ]
48247                 }
48248             ]
48249             
48250         };
48251         
48252         if (this.fieldLabel.length) {
48253             var indicator = {
48254                 tag: 'i',
48255                 tooltip: 'This field is required'
48256             };
48257
48258             var label = {
48259                 tag: 'label',
48260                 'for':  id,
48261                 cls: 'control-label',
48262                 cn: []
48263             };
48264
48265             var label_text = {
48266                 tag: 'span',
48267                 html: this.fieldLabel
48268             };
48269
48270             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48271             label.cn = [
48272                 indicator,
48273                 label_text
48274             ];
48275
48276             if(this.indicatorpos == 'right') {
48277                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48278                 label.cn = [
48279                     label_text,
48280                     indicator
48281                 ];
48282             }
48283
48284             if(align == 'left') {
48285                 container = {
48286                     tag: 'div',
48287                     cn: [
48288                         container
48289                     ]
48290                 };
48291
48292                 if(this.labelWidth > 12){
48293                     label.style = "width: " + this.labelWidth + 'px';
48294                 }
48295                 if(this.labelWidth < 13 && this.labelmd == 0){
48296                     this.labelmd = this.labelWidth;
48297                 }
48298                 if(this.labellg > 0){
48299                     label.cls += ' col-lg-' + this.labellg;
48300                     input.cls += ' col-lg-' + (12 - this.labellg);
48301                 }
48302                 if(this.labelmd > 0){
48303                     label.cls += ' col-md-' + this.labelmd;
48304                     container.cls += ' col-md-' + (12 - this.labelmd);
48305                 }
48306                 if(this.labelsm > 0){
48307                     label.cls += ' col-sm-' + this.labelsm;
48308                     container.cls += ' col-sm-' + (12 - this.labelsm);
48309                 }
48310                 if(this.labelxs > 0){
48311                     label.cls += ' col-xs-' + this.labelxs;
48312                     container.cls += ' col-xs-' + (12 - this.labelxs);
48313                 }
48314             }
48315         }
48316
48317         cfg.cn = [
48318             label,
48319             container,
48320             hiddenInput
48321         ];
48322         
48323         var settings = this;
48324
48325         ['xs','sm','md','lg'].map(function(size){
48326             if (settings[size]) {
48327                 cfg.cls += ' col-' + size + '-' + settings[size];
48328             }
48329         });
48330         
48331         return cfg;
48332     },
48333     
48334     initEvents : function()
48335     {
48336         this.indicator = this.indicatorEl();
48337         
48338         this.initCurrencyEvent();
48339         
48340         this.initNumberEvent();
48341     },
48342     
48343     initCurrencyEvent : function()
48344     {
48345         if (!this.store) {
48346             throw "can not find store for combo";
48347         }
48348         
48349         this.store = Roo.factory(this.store, Roo.data);
48350         this.store.parent = this;
48351         
48352         this.createList();
48353         
48354         this.triggerEl = this.el.select('.input-group-addon', true).first();
48355         
48356         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
48357         
48358         var _this = this;
48359         
48360         (function(){
48361             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48362             _this.list.setWidth(lw);
48363         }).defer(100);
48364         
48365         this.list.on('mouseover', this.onViewOver, this);
48366         this.list.on('mousemove', this.onViewMove, this);
48367         this.list.on('scroll', this.onViewScroll, this);
48368         
48369         if(!this.tpl){
48370             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
48371         }
48372         
48373         this.view = new Roo.View(this.list, this.tpl, {
48374             singleSelect:true, store: this.store, selectedClass: this.selectedClass
48375         });
48376         
48377         this.view.on('click', this.onViewClick, this);
48378         
48379         this.store.on('beforeload', this.onBeforeLoad, this);
48380         this.store.on('load', this.onLoad, this);
48381         this.store.on('loadexception', this.onLoadException, this);
48382         
48383         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
48384             "up" : function(e){
48385                 this.inKeyMode = true;
48386                 this.selectPrev();
48387             },
48388
48389             "down" : function(e){
48390                 if(!this.isExpanded()){
48391                     this.onTriggerClick();
48392                 }else{
48393                     this.inKeyMode = true;
48394                     this.selectNext();
48395                 }
48396             },
48397
48398             "enter" : function(e){
48399                 this.collapse();
48400                 
48401                 if(this.fireEvent("specialkey", this, e)){
48402                     this.onViewClick(false);
48403                 }
48404                 
48405                 return true;
48406             },
48407
48408             "esc" : function(e){
48409                 this.collapse();
48410             },
48411
48412             "tab" : function(e){
48413                 this.collapse();
48414                 
48415                 if(this.fireEvent("specialkey", this, e)){
48416                     this.onViewClick(false);
48417                 }
48418                 
48419                 return true;
48420             },
48421
48422             scope : this,
48423
48424             doRelay : function(foo, bar, hname){
48425                 if(hname == 'down' || this.scope.isExpanded()){
48426                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48427                 }
48428                 return true;
48429             },
48430
48431             forceKeyDown: true
48432         });
48433         
48434         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
48435         
48436     },
48437     
48438     initNumberEvent : function(e)
48439     {
48440         this.inputEl().on("keydown" , this.fireKey,  this);
48441         this.inputEl().on("focus", this.onFocus,  this);
48442         this.inputEl().on("blur", this.onBlur,  this);
48443         
48444         this.inputEl().relayEvent('keyup', this);
48445         
48446         if(this.indicator){
48447             this.indicator.addClass('invisible');
48448         }
48449  
48450         this.originalValue = this.getValue();
48451         
48452         if(this.validationEvent == 'keyup'){
48453             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
48454             this.inputEl().on('keyup', this.filterValidation, this);
48455         }
48456         else if(this.validationEvent !== false){
48457             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
48458         }
48459         
48460         if(this.selectOnFocus){
48461             this.on("focus", this.preFocus, this);
48462             
48463         }
48464         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
48465             this.inputEl().on("keypress", this.filterKeys, this);
48466         } else {
48467             this.inputEl().relayEvent('keypress', this);
48468         }
48469         
48470         var allowed = "0123456789";
48471         
48472         if(this.allowDecimals){
48473             allowed += this.decimalSeparator;
48474         }
48475         
48476         if(this.allowNegative){
48477             allowed += "-";
48478         }
48479         
48480         if(this.thousandsDelimiter) {
48481             allowed += ",";
48482         }
48483         
48484         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
48485         
48486         var keyPress = function(e){
48487             
48488             var k = e.getKey();
48489             
48490             var c = e.getCharCode();
48491             
48492             if(
48493                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
48494                     allowed.indexOf(String.fromCharCode(c)) === -1
48495             ){
48496                 e.stopEvent();
48497                 return;
48498             }
48499             
48500             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
48501                 return;
48502             }
48503             
48504             if(allowed.indexOf(String.fromCharCode(c)) === -1){
48505                 e.stopEvent();
48506             }
48507         };
48508         
48509         this.inputEl().on("keypress", keyPress, this);
48510         
48511     },
48512     
48513     onTriggerClick : function(e)
48514     {   
48515         if(this.disabled){
48516             return;
48517         }
48518         
48519         this.page = 0;
48520         this.loadNext = false;
48521         
48522         if(this.isExpanded()){
48523             this.collapse();
48524             return;
48525         }
48526         
48527         this.hasFocus = true;
48528         
48529         if(this.triggerAction == 'all') {
48530             this.doQuery(this.allQuery, true);
48531             return;
48532         }
48533         
48534         this.doQuery(this.getRawValue());
48535     },
48536     
48537     getCurrency : function()
48538     {   
48539         var v = this.currencyEl().getValue();
48540         
48541         return v;
48542     },
48543     
48544     restrictHeight : function()
48545     {
48546         this.list.alignTo(this.currencyEl(), this.listAlign);
48547         this.list.alignTo(this.currencyEl(), this.listAlign);
48548     },
48549     
48550     onViewClick : function(view, doFocus, el, e)
48551     {
48552         var index = this.view.getSelectedIndexes()[0];
48553         
48554         var r = this.store.getAt(index);
48555         
48556         if(r){
48557             this.onSelect(r, index);
48558         }
48559     },
48560     
48561     onSelect : function(record, index){
48562         
48563         if(this.fireEvent('beforeselect', this, record, index) !== false){
48564         
48565             this.setFromCurrencyData(index > -1 ? record.data : false);
48566             
48567             this.collapse();
48568             
48569             this.fireEvent('select', this, record, index);
48570         }
48571     },
48572     
48573     setFromCurrencyData : function(o)
48574     {
48575         var currency = '';
48576         
48577         this.lastCurrency = o;
48578         
48579         if (this.currencyField) {
48580             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
48581         } else {
48582             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
48583         }
48584         
48585         this.lastSelectionText = currency;
48586         
48587         //setting default currency
48588         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
48589             this.setCurrency(this.defaultCurrency);
48590             return;
48591         }
48592         
48593         this.setCurrency(currency);
48594     },
48595     
48596     setFromData : function(o)
48597     {
48598         var c = {};
48599         
48600         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
48601         
48602         this.setFromCurrencyData(c);
48603         
48604         var value = '';
48605         
48606         if (this.name) {
48607             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
48608         } else {
48609             Roo.log('no value set for '+ (this.name ? this.name : this.id));
48610         }
48611         
48612         this.setValue(value);
48613         
48614     },
48615     
48616     setCurrency : function(v)
48617     {   
48618         this.currencyValue = v;
48619         
48620         if(this.rendered){
48621             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
48622             this.validate();
48623         }
48624     },
48625     
48626     setValue : function(v)
48627     {
48628         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
48629         
48630         this.value = v;
48631         
48632         if(this.rendered){
48633             
48634             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48635             
48636             this.inputEl().dom.value = (v == '') ? '' :
48637                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
48638             
48639             if(!this.allowZero && v === '0') {
48640                 this.hiddenEl().dom.value = '';
48641                 this.inputEl().dom.value = '';
48642             }
48643             
48644             this.validate();
48645         }
48646     },
48647     
48648     getRawValue : function()
48649     {
48650         var v = this.inputEl().getValue();
48651         
48652         return v;
48653     },
48654     
48655     getValue : function()
48656     {
48657         return this.fixPrecision(this.parseValue(this.getRawValue()));
48658     },
48659     
48660     parseValue : function(value)
48661     {
48662         if(this.thousandsDelimiter) {
48663             value += "";
48664             r = new RegExp(",", "g");
48665             value = value.replace(r, "");
48666         }
48667         
48668         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
48669         return isNaN(value) ? '' : value;
48670         
48671     },
48672     
48673     fixPrecision : function(value)
48674     {
48675         if(this.thousandsDelimiter) {
48676             value += "";
48677             r = new RegExp(",", "g");
48678             value = value.replace(r, "");
48679         }
48680         
48681         var nan = isNaN(value);
48682         
48683         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
48684             return nan ? '' : value;
48685         }
48686         return parseFloat(value).toFixed(this.decimalPrecision);
48687     },
48688     
48689     decimalPrecisionFcn : function(v)
48690     {
48691         return Math.floor(v);
48692     },
48693     
48694     validateValue : function(value)
48695     {
48696         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
48697             return false;
48698         }
48699         
48700         var num = this.parseValue(value);
48701         
48702         if(isNaN(num)){
48703             this.markInvalid(String.format(this.nanText, value));
48704             return false;
48705         }
48706         
48707         if(num < this.minValue){
48708             this.markInvalid(String.format(this.minText, this.minValue));
48709             return false;
48710         }
48711         
48712         if(num > this.maxValue){
48713             this.markInvalid(String.format(this.maxText, this.maxValue));
48714             return false;
48715         }
48716         
48717         return true;
48718     },
48719     
48720     validate : function()
48721     {
48722         if(this.disabled || this.allowBlank){
48723             this.markValid();
48724             return true;
48725         }
48726         
48727         var currency = this.getCurrency();
48728         
48729         if(this.validateValue(this.getRawValue()) && currency.length){
48730             this.markValid();
48731             return true;
48732         }
48733         
48734         this.markInvalid();
48735         return false;
48736     },
48737     
48738     getName: function()
48739     {
48740         return this.name;
48741     },
48742     
48743     beforeBlur : function()
48744     {
48745         if(!this.castInt){
48746             return;
48747         }
48748         
48749         var v = this.parseValue(this.getRawValue());
48750         
48751         if(v || v == 0){
48752             this.setValue(v);
48753         }
48754     },
48755     
48756     onBlur : function()
48757     {
48758         this.beforeBlur();
48759         
48760         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
48761             //this.el.removeClass(this.focusClass);
48762         }
48763         
48764         this.hasFocus = false;
48765         
48766         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
48767             this.validate();
48768         }
48769         
48770         var v = this.getValue();
48771         
48772         if(String(v) !== String(this.startValue)){
48773             this.fireEvent('change', this, v, this.startValue);
48774         }
48775         
48776         this.fireEvent("blur", this);
48777     },
48778     
48779     inputEl : function()
48780     {
48781         return this.el.select('.roo-money-amount-input', true).first();
48782     },
48783     
48784     currencyEl : function()
48785     {
48786         return this.el.select('.roo-money-currency-input', true).first();
48787     },
48788     
48789     hiddenEl : function()
48790     {
48791         return this.el.select('input.hidden-number-input',true).first();
48792     }
48793     
48794 });/**
48795  * @class Roo.bootstrap.BezierSignature
48796  * @extends Roo.bootstrap.Component
48797  * Bootstrap BezierSignature class
48798  * This script refer to:
48799  *    Title: Signature Pad
48800  *    Author: szimek
48801  *    Availability: https://github.com/szimek/signature_pad
48802  *
48803  * @constructor
48804  * Create a new BezierSignature
48805  * @param {Object} config The config object
48806  */
48807
48808 Roo.bootstrap.BezierSignature = function(config){
48809     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
48810     this.addEvents({
48811         "resize" : true
48812     });
48813 };
48814
48815 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
48816 {
48817      
48818     curve_data: [],
48819     
48820     is_empty: true,
48821     
48822     mouse_btn_down: true,
48823     
48824     /**
48825      * @cfg {int} canvas height
48826      */
48827     canvas_height: '200px',
48828     
48829     /**
48830      * @cfg {float|function} Radius of a single dot.
48831      */ 
48832     dot_size: false,
48833     
48834     /**
48835      * @cfg {float} Minimum width of a line. Defaults to 0.5.
48836      */
48837     min_width: 0.5,
48838     
48839     /**
48840      * @cfg {float} Maximum width of a line. Defaults to 2.5.
48841      */
48842     max_width: 2.5,
48843     
48844     /**
48845      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
48846      */
48847     throttle: 16,
48848     
48849     /**
48850      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
48851      */
48852     min_distance: 5,
48853     
48854     /**
48855      * @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.
48856      */
48857     bg_color: 'rgba(0, 0, 0, 0)',
48858     
48859     /**
48860      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
48861      */
48862     dot_color: 'black',
48863     
48864     /**
48865      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
48866      */ 
48867     velocity_filter_weight: 0.7,
48868     
48869     /**
48870      * @cfg {function} Callback when stroke begin. 
48871      */
48872     onBegin: false,
48873     
48874     /**
48875      * @cfg {function} Callback when stroke end.
48876      */
48877     onEnd: false,
48878     
48879     getAutoCreate : function()
48880     {
48881         var cls = 'roo-signature column';
48882         
48883         if(this.cls){
48884             cls += ' ' + this.cls;
48885         }
48886         
48887         var col_sizes = [
48888             'lg',
48889             'md',
48890             'sm',
48891             'xs'
48892         ];
48893         
48894         for(var i = 0; i < col_sizes.length; i++) {
48895             if(this[col_sizes[i]]) {
48896                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
48897             }
48898         }
48899         
48900         var cfg = {
48901             tag: 'div',
48902             cls: cls,
48903             cn: [
48904                 {
48905                     tag: 'div',
48906                     cls: 'roo-signature-body',
48907                     cn: [
48908                         {
48909                             tag: 'canvas',
48910                             cls: 'roo-signature-body-canvas',
48911                             height: this.canvas_height,
48912                             width: this.canvas_width
48913                         }
48914                     ]
48915                 },
48916                 {
48917                     tag: 'input',
48918                     type: 'file',
48919                     style: 'display: none'
48920                 }
48921             ]
48922         };
48923         
48924         return cfg;
48925     },
48926     
48927     initEvents: function() 
48928     {
48929         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
48930         
48931         var canvas = this.canvasEl();
48932         
48933         // mouse && touch event swapping...
48934         canvas.dom.style.touchAction = 'none';
48935         canvas.dom.style.msTouchAction = 'none';
48936         
48937         this.mouse_btn_down = false;
48938         canvas.on('mousedown', this._handleMouseDown, this);
48939         canvas.on('mousemove', this._handleMouseMove, this);
48940         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
48941         
48942         if (window.PointerEvent) {
48943             canvas.on('pointerdown', this._handleMouseDown, this);
48944             canvas.on('pointermove', this._handleMouseMove, this);
48945             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
48946         }
48947         
48948         if ('ontouchstart' in window) {
48949             canvas.on('touchstart', this._handleTouchStart, this);
48950             canvas.on('touchmove', this._handleTouchMove, this);
48951             canvas.on('touchend', this._handleTouchEnd, this);
48952         }
48953         
48954         Roo.EventManager.onWindowResize(this.resize, this, true);
48955         
48956         // file input event
48957         this.fileEl().on('change', this.uploadImage, this);
48958         
48959         this.clear();
48960         
48961         this.resize();
48962     },
48963     
48964     resize: function(){
48965         
48966         var canvas = this.canvasEl().dom;
48967         var ctx = this.canvasElCtx();
48968         var img_data = false;
48969         
48970         if(canvas.width > 0) {
48971             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
48972         }
48973         // setting canvas width will clean img data
48974         canvas.width = 0;
48975         
48976         var style = window.getComputedStyle ? 
48977             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
48978             
48979         var padding_left = parseInt(style.paddingLeft) || 0;
48980         var padding_right = parseInt(style.paddingRight) || 0;
48981         
48982         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
48983         
48984         if(img_data) {
48985             ctx.putImageData(img_data, 0, 0);
48986         }
48987     },
48988     
48989     _handleMouseDown: function(e)
48990     {
48991         if (e.browserEvent.which === 1) {
48992             this.mouse_btn_down = true;
48993             this.strokeBegin(e);
48994         }
48995     },
48996     
48997     _handleMouseMove: function (e)
48998     {
48999         if (this.mouse_btn_down) {
49000             this.strokeMoveUpdate(e);
49001         }
49002     },
49003     
49004     _handleMouseUp: function (e)
49005     {
49006         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49007             this.mouse_btn_down = false;
49008             this.strokeEnd(e);
49009         }
49010     },
49011     
49012     _handleTouchStart: function (e) {
49013         
49014         e.preventDefault();
49015         if (e.browserEvent.targetTouches.length === 1) {
49016             // var touch = e.browserEvent.changedTouches[0];
49017             // this.strokeBegin(touch);
49018             
49019              this.strokeBegin(e); // assume e catching the correct xy...
49020         }
49021     },
49022     
49023     _handleTouchMove: function (e) {
49024         e.preventDefault();
49025         // var touch = event.targetTouches[0];
49026         // _this._strokeMoveUpdate(touch);
49027         this.strokeMoveUpdate(e);
49028     },
49029     
49030     _handleTouchEnd: function (e) {
49031         var wasCanvasTouched = e.target === this.canvasEl().dom;
49032         if (wasCanvasTouched) {
49033             e.preventDefault();
49034             // var touch = event.changedTouches[0];
49035             // _this._strokeEnd(touch);
49036             this.strokeEnd(e);
49037         }
49038     },
49039     
49040     reset: function () {
49041         this._lastPoints = [];
49042         this._lastVelocity = 0;
49043         this._lastWidth = (this.min_width + this.max_width) / 2;
49044         this.canvasElCtx().fillStyle = this.dot_color;
49045     },
49046     
49047     strokeMoveUpdate: function(e)
49048     {
49049         this.strokeUpdate(e);
49050         
49051         if (this.throttle) {
49052             this.throttleStroke(this.strokeUpdate, this.throttle);
49053         }
49054         else {
49055             this.strokeUpdate(e);
49056         }
49057     },
49058     
49059     strokeBegin: function(e)
49060     {
49061         var newPointGroup = {
49062             color: this.dot_color,
49063             points: []
49064         };
49065         
49066         if (typeof this.onBegin === 'function') {
49067             this.onBegin(e);
49068         }
49069         
49070         this.curve_data.push(newPointGroup);
49071         this.reset();
49072         this.strokeUpdate(e);
49073     },
49074     
49075     strokeUpdate: function(e)
49076     {
49077         var rect = this.canvasEl().dom.getBoundingClientRect();
49078         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49079         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49080         var lastPoints = lastPointGroup.points;
49081         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49082         var isLastPointTooClose = lastPoint
49083             ? point.distanceTo(lastPoint) <= this.min_distance
49084             : false;
49085         var color = lastPointGroup.color;
49086         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49087             var curve = this.addPoint(point);
49088             if (!lastPoint) {
49089                 this.drawDot({color: color, point: point});
49090             }
49091             else if (curve) {
49092                 this.drawCurve({color: color, curve: curve});
49093             }
49094             lastPoints.push({
49095                 time: point.time,
49096                 x: point.x,
49097                 y: point.y
49098             });
49099         }
49100     },
49101     
49102     strokeEnd: function(e)
49103     {
49104         this.strokeUpdate(e);
49105         if (typeof this.onEnd === 'function') {
49106             this.onEnd(e);
49107         }
49108     },
49109     
49110     addPoint:  function (point) {
49111         var _lastPoints = this._lastPoints;
49112         _lastPoints.push(point);
49113         if (_lastPoints.length > 2) {
49114             if (_lastPoints.length === 3) {
49115                 _lastPoints.unshift(_lastPoints[0]);
49116             }
49117             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49118             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49119             _lastPoints.shift();
49120             return curve;
49121         }
49122         return null;
49123     },
49124     
49125     calculateCurveWidths: function (startPoint, endPoint) {
49126         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49127             (1 - this.velocity_filter_weight) * this._lastVelocity;
49128
49129         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49130         var widths = {
49131             end: newWidth,
49132             start: this._lastWidth
49133         };
49134         
49135         this._lastVelocity = velocity;
49136         this._lastWidth = newWidth;
49137         return widths;
49138     },
49139     
49140     drawDot: function (_a) {
49141         var color = _a.color, point = _a.point;
49142         var ctx = this.canvasElCtx();
49143         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49144         ctx.beginPath();
49145         this.drawCurveSegment(point.x, point.y, width);
49146         ctx.closePath();
49147         ctx.fillStyle = color;
49148         ctx.fill();
49149     },
49150     
49151     drawCurve: function (_a) {
49152         var color = _a.color, curve = _a.curve;
49153         var ctx = this.canvasElCtx();
49154         var widthDelta = curve.endWidth - curve.startWidth;
49155         var drawSteps = Math.floor(curve.length()) * 2;
49156         ctx.beginPath();
49157         ctx.fillStyle = color;
49158         for (var i = 0; i < drawSteps; i += 1) {
49159         var t = i / drawSteps;
49160         var tt = t * t;
49161         var ttt = tt * t;
49162         var u = 1 - t;
49163         var uu = u * u;
49164         var uuu = uu * u;
49165         var x = uuu * curve.startPoint.x;
49166         x += 3 * uu * t * curve.control1.x;
49167         x += 3 * u * tt * curve.control2.x;
49168         x += ttt * curve.endPoint.x;
49169         var y = uuu * curve.startPoint.y;
49170         y += 3 * uu * t * curve.control1.y;
49171         y += 3 * u * tt * curve.control2.y;
49172         y += ttt * curve.endPoint.y;
49173         var width = curve.startWidth + ttt * widthDelta;
49174         this.drawCurveSegment(x, y, width);
49175         }
49176         ctx.closePath();
49177         ctx.fill();
49178     },
49179     
49180     drawCurveSegment: function (x, y, width) {
49181         var ctx = this.canvasElCtx();
49182         ctx.moveTo(x, y);
49183         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
49184         this.is_empty = false;
49185     },
49186     
49187     clear: function()
49188     {
49189         var ctx = this.canvasElCtx();
49190         var canvas = this.canvasEl().dom;
49191         ctx.fillStyle = this.bg_color;
49192         ctx.clearRect(0, 0, canvas.width, canvas.height);
49193         ctx.fillRect(0, 0, canvas.width, canvas.height);
49194         this.curve_data = [];
49195         this.reset();
49196         this.is_empty = true;
49197     },
49198     
49199     fileEl: function()
49200     {
49201         return  this.el.select('input',true).first();
49202     },
49203     
49204     canvasEl: function()
49205     {
49206         return this.el.select('canvas',true).first();
49207     },
49208     
49209     canvasElCtx: function()
49210     {
49211         return this.el.select('canvas',true).first().dom.getContext('2d');
49212     },
49213     
49214     getImage: function(type)
49215     {
49216         if(this.is_empty) {
49217             return false;
49218         }
49219         
49220         // encryption ?
49221         return this.canvasEl().dom.toDataURL('image/'+type, 1);
49222     },
49223     
49224     drawFromImage: function(img_src)
49225     {
49226         var img = new Image();
49227         
49228         img.onload = function(){
49229             this.canvasElCtx().drawImage(img, 0, 0);
49230         }.bind(this);
49231         
49232         img.src = img_src;
49233         
49234         this.is_empty = false;
49235     },
49236     
49237     selectImage: function()
49238     {
49239         this.fileEl().dom.click();
49240     },
49241     
49242     uploadImage: function(e)
49243     {
49244         var reader = new FileReader();
49245         
49246         reader.onload = function(e){
49247             var img = new Image();
49248             img.onload = function(){
49249                 this.reset();
49250                 this.canvasElCtx().drawImage(img, 0, 0);
49251             }.bind(this);
49252             img.src = e.target.result;
49253         }.bind(this);
49254         
49255         reader.readAsDataURL(e.target.files[0]);
49256     },
49257     
49258     // Bezier Point Constructor
49259     Point: (function () {
49260         function Point(x, y, time) {
49261             this.x = x;
49262             this.y = y;
49263             this.time = time || Date.now();
49264         }
49265         Point.prototype.distanceTo = function (start) {
49266             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
49267         };
49268         Point.prototype.equals = function (other) {
49269             return this.x === other.x && this.y === other.y && this.time === other.time;
49270         };
49271         Point.prototype.velocityFrom = function (start) {
49272             return this.time !== start.time
49273             ? this.distanceTo(start) / (this.time - start.time)
49274             : 0;
49275         };
49276         return Point;
49277     }()),
49278     
49279     
49280     // Bezier Constructor
49281     Bezier: (function () {
49282         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
49283             this.startPoint = startPoint;
49284             this.control2 = control2;
49285             this.control1 = control1;
49286             this.endPoint = endPoint;
49287             this.startWidth = startWidth;
49288             this.endWidth = endWidth;
49289         }
49290         Bezier.fromPoints = function (points, widths, scope) {
49291             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
49292             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
49293             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
49294         };
49295         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
49296             var dx1 = s1.x - s2.x;
49297             var dy1 = s1.y - s2.y;
49298             var dx2 = s2.x - s3.x;
49299             var dy2 = s2.y - s3.y;
49300             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
49301             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
49302             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
49303             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
49304             var dxm = m1.x - m2.x;
49305             var dym = m1.y - m2.y;
49306             var k = l2 / (l1 + l2);
49307             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
49308             var tx = s2.x - cm.x;
49309             var ty = s2.y - cm.y;
49310             return {
49311                 c1: new scope.Point(m1.x + tx, m1.y + ty),
49312                 c2: new scope.Point(m2.x + tx, m2.y + ty)
49313             };
49314         };
49315         Bezier.prototype.length = function () {
49316             var steps = 10;
49317             var length = 0;
49318             var px;
49319             var py;
49320             for (var i = 0; i <= steps; i += 1) {
49321                 var t = i / steps;
49322                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
49323                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
49324                 if (i > 0) {
49325                     var xdiff = cx - px;
49326                     var ydiff = cy - py;
49327                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
49328                 }
49329                 px = cx;
49330                 py = cy;
49331             }
49332             return length;
49333         };
49334         Bezier.prototype.point = function (t, start, c1, c2, end) {
49335             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
49336             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
49337             + (3.0 * c2 * (1.0 - t) * t * t)
49338             + (end * t * t * t);
49339         };
49340         return Bezier;
49341     }()),
49342     
49343     throttleStroke: function(fn, wait) {
49344       if (wait === void 0) { wait = 250; }
49345       var previous = 0;
49346       var timeout = null;
49347       var result;
49348       var storedContext;
49349       var storedArgs;
49350       var later = function () {
49351           previous = Date.now();
49352           timeout = null;
49353           result = fn.apply(storedContext, storedArgs);
49354           if (!timeout) {
49355               storedContext = null;
49356               storedArgs = [];
49357           }
49358       };
49359       return function wrapper() {
49360           var args = [];
49361           for (var _i = 0; _i < arguments.length; _i++) {
49362               args[_i] = arguments[_i];
49363           }
49364           var now = Date.now();
49365           var remaining = wait - (now - previous);
49366           storedContext = this;
49367           storedArgs = args;
49368           if (remaining <= 0 || remaining > wait) {
49369               if (timeout) {
49370                   clearTimeout(timeout);
49371                   timeout = null;
49372               }
49373               previous = now;
49374               result = fn.apply(storedContext, storedArgs);
49375               if (!timeout) {
49376                   storedContext = null;
49377                   storedArgs = [];
49378               }
49379           }
49380           else if (!timeout) {
49381               timeout = window.setTimeout(later, remaining);
49382           }
49383           return result;
49384       };
49385   }
49386   
49387 });
49388
49389  
49390
49391  // old names for form elements
49392 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
49393 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
49394 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
49395 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
49396 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
49397 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
49398 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
49399 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
49400 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
49401 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
49402 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
49403 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
49404 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
49405 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
49406 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
49407 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
49408 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
49409 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
49410 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
49411 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
49412 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
49413 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
49414 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
49415 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
49416 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
49417 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
49418
49419 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
49420 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
49421
49422 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
49423 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
49424
49425 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
49426 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
49427 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
49428 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
49429