buildSDK/dependancy_bootstrap.txt
[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} headerShow (true|false) generate thead, default true
9161  * @cfg {Boolean} rowSelection (true|false) default false
9162  * @cfg {Boolean} cellSelection (true|false) default false
9163  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9164  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9165  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9166  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9167  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9168  * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS()
9169  *
9170  * 
9171  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9172  * 
9173  * @constructor
9174  * Create a new Table
9175  * @param {Object} config The config object
9176  */
9177
9178 Roo.bootstrap.Table = function(config)
9179 {
9180     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9181      
9182     // BC...
9183     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9184     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9185     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9186     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9187     
9188     this.view = this; // compat with grid.
9189     
9190     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9191     if (this.sm) {
9192         this.sm.grid = this;
9193         this.selModel = Roo.factory(this.sm, Roo.grid);
9194         this.sm = this.selModel;
9195         this.sm.xmodule = this.xmodule || false;
9196     }
9197     
9198     if (this.cm && typeof(this.cm.config) == 'undefined') {
9199         this.colModel = new Roo.grid.ColumnModel(this.cm);
9200         this.cm = this.colModel;
9201         this.cm.xmodule = this.xmodule || false;
9202     }
9203     if (this.store) {
9204         this.store= Roo.factory(this.store, Roo.data);
9205         this.ds = this.store;
9206         this.ds.xmodule = this.xmodule || false;
9207          
9208     }
9209     if (this.footer && this.store) {
9210         this.footer.dataSource = this.ds;
9211         this.footer = Roo.factory(this.footer);
9212     }
9213     
9214     /** @private */
9215     this.addEvents({
9216         /**
9217          * @event cellclick
9218          * Fires when a cell is clicked
9219          * @param {Roo.bootstrap.Table} this
9220          * @param {Roo.Element} el
9221          * @param {Number} rowIndex
9222          * @param {Number} columnIndex
9223          * @param {Roo.EventObject} e
9224          */
9225         "cellclick" : true,
9226         /**
9227          * @event celldblclick
9228          * Fires when a cell is double clicked
9229          * @param {Roo.bootstrap.Table} this
9230          * @param {Roo.Element} el
9231          * @param {Number} rowIndex
9232          * @param {Number} columnIndex
9233          * @param {Roo.EventObject} e
9234          */
9235         "celldblclick" : true,
9236         /**
9237          * @event rowclick
9238          * Fires when a row is clicked
9239          * @param {Roo.bootstrap.Table} this
9240          * @param {Roo.Element} el
9241          * @param {Number} rowIndex
9242          * @param {Roo.EventObject} e
9243          */
9244         "rowclick" : true,
9245         /**
9246          * @event rowdblclick
9247          * Fires when a row is double clicked
9248          * @param {Roo.bootstrap.Table} this
9249          * @param {Roo.Element} el
9250          * @param {Number} rowIndex
9251          * @param {Roo.EventObject} e
9252          */
9253         "rowdblclick" : true,
9254         /**
9255          * @event mouseover
9256          * Fires when a mouseover occur
9257          * @param {Roo.bootstrap.Table} this
9258          * @param {Roo.Element} el
9259          * @param {Number} rowIndex
9260          * @param {Number} columnIndex
9261          * @param {Roo.EventObject} e
9262          */
9263         "mouseover" : true,
9264         /**
9265          * @event mouseout
9266          * Fires when a mouseout occur
9267          * @param {Roo.bootstrap.Table} this
9268          * @param {Roo.Element} el
9269          * @param {Number} rowIndex
9270          * @param {Number} columnIndex
9271          * @param {Roo.EventObject} e
9272          */
9273         "mouseout" : true,
9274         /**
9275          * @event rowclass
9276          * Fires when a row is rendered, so you can change add a style to it.
9277          * @param {Roo.bootstrap.Table} this
9278          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9279          */
9280         'rowclass' : true,
9281           /**
9282          * @event rowsrendered
9283          * Fires when all the  rows have been rendered
9284          * @param {Roo.bootstrap.Table} this
9285          */
9286         'rowsrendered' : true,
9287         /**
9288          * @event contextmenu
9289          * The raw contextmenu event for the entire grid.
9290          * @param {Roo.EventObject} e
9291          */
9292         "contextmenu" : true,
9293         /**
9294          * @event rowcontextmenu
9295          * Fires when a row is right clicked
9296          * @param {Roo.bootstrap.Table} this
9297          * @param {Number} rowIndex
9298          * @param {Roo.EventObject} e
9299          */
9300         "rowcontextmenu" : true,
9301         /**
9302          * @event cellcontextmenu
9303          * Fires when a cell is right clicked
9304          * @param {Roo.bootstrap.Table} this
9305          * @param {Number} rowIndex
9306          * @param {Number} cellIndex
9307          * @param {Roo.EventObject} e
9308          */
9309          "cellcontextmenu" : true,
9310          /**
9311          * @event headercontextmenu
9312          * Fires when a header is right clicked
9313          * @param {Roo.bootstrap.Table} this
9314          * @param {Number} columnIndex
9315          * @param {Roo.EventObject} e
9316          */
9317         "headercontextmenu" : true,
9318         /**
9319          * @event mousedown
9320          * The raw mousedown event for the entire grid.
9321          * @param {Roo.EventObject} e
9322          */
9323         "mousedown" : true
9324         
9325     });
9326 };
9327
9328 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9329     
9330     cls: false,
9331     
9332     empty_results : '',
9333     striped : false,
9334     scrollBody : false,
9335     bordered: false,
9336     hover:  false,
9337     condensed : false,
9338     responsive : false,
9339     sm : false,
9340     cm : false,
9341     store : false,
9342     loadMask : false,
9343     footerShow : true,
9344     headerShow : true,
9345     enableColumnResize: true,
9346     disableAutoSize: false,
9347   
9348     rowSelection : false,
9349     cellSelection : false,
9350     layout : false,
9351
9352     minColumnWidth : 50,
9353     
9354     // Roo.Element - the tbody
9355     bodyEl: false,  // <tbody> Roo.Element - thead element    
9356     headEl: false,  // <thead> Roo.Element - thead element
9357     resizeProxy : false, // proxy element for dragging?
9358
9359
9360     
9361     container: false, // used by gridpanel...
9362     
9363     lazyLoad : false,
9364     
9365     CSS : Roo.util.CSS,
9366     
9367     auto_hide_footer : false,
9368     
9369     view: false, // actually points to this..
9370     
9371     getAutoCreate : function()
9372     {
9373         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9374         
9375         cfg = {
9376             tag: 'table',
9377             cls : 'table', 
9378             cn : []
9379         };
9380         // this get's auto added by panel.Grid
9381         if (this.scrollBody) {
9382             cfg.cls += ' table-body-fixed';
9383         }    
9384         if (this.striped) {
9385             cfg.cls += ' table-striped';
9386         }
9387         
9388         if (this.hover) {
9389             cfg.cls += ' table-hover';
9390         }
9391         if (this.bordered) {
9392             cfg.cls += ' table-bordered';
9393         }
9394         if (this.condensed) {
9395             cfg.cls += ' table-condensed';
9396         }
9397         
9398         if (this.responsive) {
9399             cfg.cls += ' table-responsive';
9400         }
9401         
9402         if (this.cls) {
9403             cfg.cls+=  ' ' +this.cls;
9404         }
9405         
9406         
9407         
9408         if (this.layout) {
9409             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9410         }
9411         
9412         if(this.store || this.cm){
9413             if(this.headerShow){
9414                 cfg.cn.push(this.renderHeader());
9415             }
9416             
9417             cfg.cn.push(this.renderBody());
9418             
9419             if(this.footerShow){
9420                 cfg.cn.push(this.renderFooter());
9421             }
9422             // where does this come from?
9423             //cfg.cls+=  ' TableGrid';
9424         }
9425         
9426         return { cn : [ cfg ] };
9427     },
9428     
9429     initEvents : function()
9430     {   
9431         if(!this.store || !this.cm){
9432             return;
9433         }
9434         if (this.selModel) {
9435             this.selModel.initEvents();
9436         }
9437         
9438         
9439         //Roo.log('initEvents with ds!!!!');
9440         
9441         this.bodyEl = this.el.select('tbody', true).first();
9442         this.headEl = this.el.select('thead', true).first();
9443         this.mainFoot = this.el.select('tfoot', true).first();
9444         
9445         
9446         
9447         
9448         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9449             e.on('click', this.sort, this);
9450         }, this);
9451         
9452         
9453         // why is this done????? = it breaks dialogs??
9454         //this.parent().el.setStyle('position', 'relative');
9455         
9456         
9457         if (this.footer) {
9458             this.footer.parentId = this.id;
9459             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9460             
9461             if(this.lazyLoad){
9462                 this.el.select('tfoot tr td').first().addClass('hide');
9463             }
9464         } 
9465         
9466         if(this.loadMask) {
9467             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9468         }
9469         
9470         this.store.on('load', this.onLoad, this);
9471         this.store.on('beforeload', this.onBeforeLoad, this);
9472         this.store.on('update', this.onUpdate, this);
9473         this.store.on('add', this.onAdd, this);
9474         this.store.on("clear", this.clear, this);
9475         
9476         this.el.on("contextmenu", this.onContextMenu, this);
9477         
9478         
9479         this.cm.on("headerchange", this.onHeaderChange, this);
9480         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9481
9482  //?? does bodyEl get replaced on render?
9483         this.bodyEl.on("click", this.onClick, this);
9484         this.bodyEl.on("dblclick", this.onDblClick, this);        
9485         this.bodyEl.on('scroll', this.onBodyScroll, this);
9486
9487         // guessing mainbody will work - this relays usually caught by selmodel at present.
9488         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9489   
9490   
9491         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9492         
9493   
9494         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9495             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9496         }
9497         
9498         this.initCSS();
9499     },
9500     // Compatibility with grid - we implement all the view features at present.
9501     getView : function()
9502     {
9503         return this;
9504     },
9505     
9506     initCSS : function()
9507     {
9508         if(this.disableAutoSize) {
9509             return;
9510         }
9511         
9512         var cm = this.cm, styles = [];
9513         this.CSS.removeStyleSheet(this.id + '-cssrules');
9514         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9515         // we can honour xs/sm/md/xl  as widths...
9516         // we first have to decide what widht we are currently at...
9517         var sz = Roo.getGridSize();
9518         
9519         var total = 0;
9520         var last = -1;
9521         var cols = []; // visable cols.
9522         var total_abs = 0;
9523         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9524             var w = cm.getColumnWidth(i, false);
9525             if(cm.isHidden(i)){
9526                 cols.push( { rel : false, abs : 0 });
9527                 continue;
9528             }
9529             if (w !== false) {
9530                 cols.push( { rel : false, abs : w });
9531                 total_abs += w;
9532                 last = i; // not really..
9533                 continue;
9534             }
9535             var w = cm.getColumnWidth(i, sz);
9536             if (w > 0) {
9537                 last = i
9538             }
9539             total += w;
9540             cols.push( { rel : w, abs : false });
9541         }
9542         
9543         var avail = this.bodyEl.dom.clientWidth - total_abs;
9544         
9545         var unitWidth = Math.floor(avail / total);
9546         var rem = avail - (unitWidth * total);
9547         
9548         var hidden, width, pos = 0 , splithide , left;
9549         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9550             
9551             hidden = 'display:none;';
9552             left = '';
9553             width  = 'width:0px;';
9554             splithide = '';
9555             if(!cm.isHidden(i)){
9556                 hidden = '';
9557                 
9558                 
9559                 // we can honour xs/sm/md/xl ?
9560                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9561                 if (w===0) {
9562                     hidden = 'display:none;';
9563                 }
9564                 // width should return a small number...
9565                 if (i == last) {
9566                     w+=rem; // add the remaining with..
9567                 }
9568                 pos += w;
9569                 left = "left:" + (pos -4) + "px;";
9570                 width = "width:" + w+ "px;";
9571                 
9572             }
9573             if (this.responsive) {
9574                 width = '';
9575                 left = '';
9576                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9577                 splithide = 'display: none;';
9578             }
9579             
9580             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9581             if (this.headEl) {
9582                 if (i == last) {
9583                     splithide = 'display:none;';
9584                 }
9585                 
9586                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9587                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9588                             // this is the popover version..
9589                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9590                 );
9591             }
9592             
9593         }
9594         //Roo.log(styles.join(''));
9595         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9596         
9597     },
9598     
9599     
9600     
9601     onContextMenu : function(e, t)
9602     {
9603         this.processEvent("contextmenu", e);
9604     },
9605     
9606     processEvent : function(name, e)
9607     {
9608         if (name != 'touchstart' ) {
9609             this.fireEvent(name, e);    
9610         }
9611         
9612         var t = e.getTarget();
9613         
9614         var cell = Roo.get(t);
9615         
9616         if(!cell){
9617             return;
9618         }
9619         
9620         if(cell.findParent('tfoot', false, true)){
9621             return;
9622         }
9623         
9624         if(cell.findParent('thead', false, true)){
9625             
9626             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9627                 cell = Roo.get(t).findParent('th', false, true);
9628                 if (!cell) {
9629                     Roo.log("failed to find th in thead?");
9630                     Roo.log(e.getTarget());
9631                     return;
9632                 }
9633             }
9634             
9635             var cellIndex = cell.dom.cellIndex;
9636             
9637             var ename = name == 'touchstart' ? 'click' : name;
9638             this.fireEvent("header" + ename, this, cellIndex, e);
9639             
9640             return;
9641         }
9642         
9643         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9644             cell = Roo.get(t).findParent('td', false, true);
9645             if (!cell) {
9646                 Roo.log("failed to find th in tbody?");
9647                 Roo.log(e.getTarget());
9648                 return;
9649             }
9650         }
9651         
9652         var row = cell.findParent('tr', false, true);
9653         var cellIndex = cell.dom.cellIndex;
9654         var rowIndex = row.dom.rowIndex - 1;
9655         
9656         if(row !== false){
9657             
9658             this.fireEvent("row" + name, this, rowIndex, e);
9659             
9660             if(cell !== false){
9661             
9662                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9663             }
9664         }
9665         
9666     },
9667     
9668     onMouseover : function(e, el)
9669     {
9670         var cell = Roo.get(el);
9671         
9672         if(!cell){
9673             return;
9674         }
9675         
9676         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9677             cell = cell.findParent('td', false, true);
9678         }
9679         
9680         var row = cell.findParent('tr', false, true);
9681         var cellIndex = cell.dom.cellIndex;
9682         var rowIndex = row.dom.rowIndex - 1; // start from 0
9683         
9684         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9685         
9686     },
9687     
9688     onMouseout : function(e, el)
9689     {
9690         var cell = Roo.get(el);
9691         
9692         if(!cell){
9693             return;
9694         }
9695         
9696         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9697             cell = cell.findParent('td', false, true);
9698         }
9699         
9700         var row = cell.findParent('tr', false, true);
9701         var cellIndex = cell.dom.cellIndex;
9702         var rowIndex = row.dom.rowIndex - 1; // start from 0
9703         
9704         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9705         
9706     },
9707     
9708     onClick : function(e, el)
9709     {
9710         var cell = Roo.get(el);
9711         
9712         if(!cell || (!this.cellSelection && !this.rowSelection)){
9713             return;
9714         }
9715         
9716         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9717             cell = cell.findParent('td', false, true);
9718         }
9719         
9720         if(!cell || typeof(cell) == 'undefined'){
9721             return;
9722         }
9723         
9724         var row = cell.findParent('tr', false, true);
9725         
9726         if(!row || typeof(row) == 'undefined'){
9727             return;
9728         }
9729         
9730         var cellIndex = cell.dom.cellIndex;
9731         var rowIndex = this.getRowIndex(row);
9732         
9733         // why??? - should these not be based on SelectionModel?
9734         //if(this.cellSelection){
9735             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9736         //}
9737         
9738         //if(this.rowSelection){
9739             this.fireEvent('rowclick', this, row, rowIndex, e);
9740         //}
9741          
9742     },
9743         
9744     onDblClick : function(e,el)
9745     {
9746         var cell = Roo.get(el);
9747         
9748         if(!cell || (!this.cellSelection && !this.rowSelection)){
9749             return;
9750         }
9751         
9752         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9753             cell = cell.findParent('td', false, true);
9754         }
9755         
9756         if(!cell || typeof(cell) == 'undefined'){
9757             return;
9758         }
9759         
9760         var row = cell.findParent('tr', false, true);
9761         
9762         if(!row || typeof(row) == 'undefined'){
9763             return;
9764         }
9765         
9766         var cellIndex = cell.dom.cellIndex;
9767         var rowIndex = this.getRowIndex(row);
9768         
9769         if(this.cellSelection){
9770             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9771         }
9772         
9773         if(this.rowSelection){
9774             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9775         }
9776     },
9777     findRowIndex : function(el)
9778     {
9779         var cell = Roo.get(el);
9780         if(!cell) {
9781             return false;
9782         }
9783         var row = cell.findParent('tr', false, true);
9784         
9785         if(!row || typeof(row) == 'undefined'){
9786             return false;
9787         }
9788         return this.getRowIndex(row);
9789     },
9790     sort : function(e,el)
9791     {
9792         var col = Roo.get(el);
9793         
9794         if(!col.hasClass('sortable')){
9795             return;
9796         }
9797         
9798         var sort = col.attr('sort');
9799         var dir = 'ASC';
9800         
9801         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9802             dir = 'DESC';
9803         }
9804         
9805         this.store.sortInfo = {field : sort, direction : dir};
9806         
9807         if (this.footer) {
9808             Roo.log("calling footer first");
9809             this.footer.onClick('first');
9810         } else {
9811         
9812             this.store.load({ params : { start : 0 } });
9813         }
9814     },
9815     
9816     renderHeader : function()
9817     {
9818         var header = {
9819             tag: 'thead',
9820             cn : []
9821         };
9822         
9823         var cm = this.cm;
9824         this.totalWidth = 0;
9825         
9826         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9827             
9828             var config = cm.config[i];
9829             
9830             var c = {
9831                 tag: 'th',
9832                 cls : 'x-hcol-' + i,
9833                 style : '',
9834                 
9835                 html: cm.getColumnHeader(i)
9836             };
9837             
9838             var tooltip = cm.getColumnTooltip(i);
9839             if (tooltip) {
9840                 c.tooltip = tooltip;
9841             }
9842             
9843             
9844             var hh = '';
9845             
9846             if(typeof(config.sortable) != 'undefined' && config.sortable){
9847                 c.cls += ' sortable';
9848                 c.html = '<i class="fa"></i>' + c.html;
9849             }
9850             
9851             // could use BS4 hidden-..-down 
9852             
9853             if(typeof(config.lgHeader) != 'undefined'){
9854                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9855             }
9856             
9857             if(typeof(config.mdHeader) != 'undefined'){
9858                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9859             }
9860             
9861             if(typeof(config.smHeader) != 'undefined'){
9862                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9863             }
9864             
9865             if(typeof(config.xsHeader) != 'undefined'){
9866                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9867             }
9868             
9869             if(hh.length){
9870                 c.html = hh;
9871             }
9872             
9873             if(typeof(config.tooltip) != 'undefined'){
9874                 c.tooltip = config.tooltip;
9875             }
9876             
9877             if(typeof(config.colspan) != 'undefined'){
9878                 c.colspan = config.colspan;
9879             }
9880             
9881             // hidden is handled by CSS now
9882             
9883             if(typeof(config.dataIndex) != 'undefined'){
9884                 c.sort = config.dataIndex;
9885             }
9886             
9887            
9888             
9889             if(typeof(config.align) != 'undefined' && config.align.length){
9890                 c.style += ' text-align:' + config.align + ';';
9891             }
9892             
9893             /* width is done in CSS
9894              *if(typeof(config.width) != 'undefined'){
9895                 c.style += ' width:' + config.width + 'px;';
9896                 this.totalWidth += config.width;
9897             } else {
9898                 this.totalWidth += 100; // assume minimum of 100 per column?
9899             }
9900             */
9901             
9902             if(typeof(config.cls) != 'undefined'){
9903                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9904             }
9905             // this is the bit that doesnt reall work at all...
9906             
9907             if (this.responsive) {
9908                  
9909             
9910                 ['xs','sm','md','lg'].map(function(size){
9911                     
9912                     if(typeof(config[size]) == 'undefined'){
9913                         return;
9914                     }
9915                      
9916                     if (!config[size]) { // 0 = hidden
9917                         // BS 4 '0' is treated as hide that column and below.
9918                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9919                         return;
9920                     }
9921                     
9922                     c.cls += ' col-' + size + '-' + config[size] + (
9923                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9924                     );
9925                     
9926                     
9927                 });
9928             }
9929             // at the end?
9930             
9931             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9932             
9933             
9934             
9935             
9936             header.cn.push(c)
9937         }
9938         
9939         return header;
9940     },
9941     
9942     renderBody : function()
9943     {
9944         var body = {
9945             tag: 'tbody',
9946             cn : [
9947                 {
9948                     tag: 'tr',
9949                     cn : [
9950                         {
9951                             tag : 'td',
9952                             colspan :  this.cm.getColumnCount()
9953                         }
9954                     ]
9955                 }
9956             ]
9957         };
9958         
9959         return body;
9960     },
9961     
9962     renderFooter : function()
9963     {
9964         var footer = {
9965             tag: 'tfoot',
9966             cn : [
9967                 {
9968                     tag: 'tr',
9969                     cn : [
9970                         {
9971                             tag : 'td',
9972                             colspan :  this.cm.getColumnCount()
9973                         }
9974                     ]
9975                 }
9976             ]
9977         };
9978         
9979         return footer;
9980     },
9981     
9982     
9983     
9984     onLoad : function()
9985     {
9986 //        Roo.log('ds onload');
9987         this.clear();
9988         
9989         var _this = this;
9990         var cm = this.cm;
9991         var ds = this.store;
9992         
9993         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9994             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9995             if (_this.store.sortInfo) {
9996                     
9997                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9998                     e.select('i', true).addClass(['fa-arrow-up']);
9999                 }
10000                 
10001                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10002                     e.select('i', true).addClass(['fa-arrow-down']);
10003                 }
10004             }
10005         });
10006         
10007         var tbody =  this.bodyEl;
10008               
10009         if(ds.getCount() > 0){
10010             ds.data.each(function(d,rowIndex){
10011                 var row =  this.renderRow(cm, ds, rowIndex);
10012                 
10013                 tbody.createChild(row);
10014                 
10015                 var _this = this;
10016                 
10017                 if(row.cellObjects.length){
10018                     Roo.each(row.cellObjects, function(r){
10019                         _this.renderCellObject(r);
10020                     })
10021                 }
10022                 
10023             }, this);
10024         } else if (this.empty_results.length) {
10025             this.el.mask(this.empty_results, 'no-spinner');
10026         }
10027         
10028         var tfoot = this.el.select('tfoot', true).first();
10029         
10030         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10031             
10032             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10033             
10034             var total = this.ds.getTotalCount();
10035             
10036             if(this.footer.pageSize < total){
10037                 this.mainFoot.show();
10038             }
10039         }
10040         
10041         Roo.each(this.el.select('tbody td', true).elements, function(e){
10042             e.on('mouseover', _this.onMouseover, _this);
10043         });
10044         
10045         Roo.each(this.el.select('tbody td', true).elements, function(e){
10046             e.on('mouseout', _this.onMouseout, _this);
10047         });
10048         this.fireEvent('rowsrendered', this);
10049         
10050         this.autoSize();
10051         
10052         this.initCSS(); /// resize cols
10053
10054         
10055     },
10056     
10057     
10058     onUpdate : function(ds,record)
10059     {
10060         this.refreshRow(record);
10061         this.autoSize();
10062     },
10063     
10064     onRemove : function(ds, record, index, isUpdate){
10065         if(isUpdate !== true){
10066             this.fireEvent("beforerowremoved", this, index, record);
10067         }
10068         var bt = this.bodyEl.dom;
10069         
10070         var rows = this.el.select('tbody > tr', true).elements;
10071         
10072         if(typeof(rows[index]) != 'undefined'){
10073             bt.removeChild(rows[index].dom);
10074         }
10075         
10076 //        if(bt.rows[index]){
10077 //            bt.removeChild(bt.rows[index]);
10078 //        }
10079         
10080         if(isUpdate !== true){
10081             //this.stripeRows(index);
10082             //this.syncRowHeights(index, index);
10083             //this.layout();
10084             this.fireEvent("rowremoved", this, index, record);
10085         }
10086     },
10087     
10088     onAdd : function(ds, records, rowIndex)
10089     {
10090         //Roo.log('on Add called');
10091         // - note this does not handle multiple adding very well..
10092         var bt = this.bodyEl.dom;
10093         for (var i =0 ; i < records.length;i++) {
10094             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10095             //Roo.log(records[i]);
10096             //Roo.log(this.store.getAt(rowIndex+i));
10097             this.insertRow(this.store, rowIndex + i, false);
10098             return;
10099         }
10100         
10101     },
10102     
10103     
10104     refreshRow : function(record){
10105         var ds = this.store, index;
10106         if(typeof record == 'number'){
10107             index = record;
10108             record = ds.getAt(index);
10109         }else{
10110             index = ds.indexOf(record);
10111             if (index < 0) {
10112                 return; // should not happen - but seems to 
10113             }
10114         }
10115         this.insertRow(ds, index, true);
10116         this.autoSize();
10117         this.onRemove(ds, record, index+1, true);
10118         this.autoSize();
10119         //this.syncRowHeights(index, index);
10120         //this.layout();
10121         this.fireEvent("rowupdated", this, index, record);
10122     },
10123     // private - called by RowSelection
10124     onRowSelect : function(rowIndex){
10125         var row = this.getRowDom(rowIndex);
10126         row.addClass(['bg-info','info']);
10127     },
10128     // private - called by RowSelection
10129     onRowDeselect : function(rowIndex)
10130     {
10131         if (rowIndex < 0) {
10132             return;
10133         }
10134         var row = this.getRowDom(rowIndex);
10135         row.removeClass(['bg-info','info']);
10136     },
10137       /**
10138      * Focuses the specified row.
10139      * @param {Number} row The row index
10140      */
10141     focusRow : function(row)
10142     {
10143         //Roo.log('GridView.focusRow');
10144         var x = this.bodyEl.dom.scrollLeft;
10145         this.focusCell(row, 0, false);
10146         this.bodyEl.dom.scrollLeft = x;
10147
10148     },
10149      /**
10150      * Focuses the specified cell.
10151      * @param {Number} row The row index
10152      * @param {Number} col The column index
10153      * @param {Boolean} hscroll false to disable horizontal scrolling
10154      */
10155     focusCell : function(row, col, hscroll)
10156     {
10157         //Roo.log('GridView.focusCell');
10158         var el = this.ensureVisible(row, col, hscroll);
10159         // not sure what focusEL achives = it's a <a> pos relative 
10160         //this.focusEl.alignTo(el, "tl-tl");
10161         //if(Roo.isGecko){
10162         //    this.focusEl.focus();
10163         //}else{
10164         //    this.focusEl.focus.defer(1, this.focusEl);
10165         //}
10166     },
10167     
10168      /**
10169      * Scrolls the specified cell into view
10170      * @param {Number} row The row index
10171      * @param {Number} col The column index
10172      * @param {Boolean} hscroll false to disable horizontal scrolling
10173      */
10174     ensureVisible : function(row, col, hscroll)
10175     {
10176         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10177         //return null; //disable for testing.
10178         if(typeof row != "number"){
10179             row = row.rowIndex;
10180         }
10181         if(row < 0 && row >= this.ds.getCount()){
10182             return  null;
10183         }
10184         col = (col !== undefined ? col : 0);
10185         var cm = this.cm;
10186         while(cm.isHidden(col)){
10187             col++;
10188         }
10189
10190         var el = this.getCellDom(row, col);
10191         if(!el){
10192             return null;
10193         }
10194         var c = this.bodyEl.dom;
10195
10196         var ctop = parseInt(el.offsetTop, 10);
10197         var cleft = parseInt(el.offsetLeft, 10);
10198         var cbot = ctop + el.offsetHeight;
10199         var cright = cleft + el.offsetWidth;
10200
10201         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10202         var ch = 0; //?? header is not withing the area?
10203         var stop = parseInt(c.scrollTop, 10);
10204         var sleft = parseInt(c.scrollLeft, 10);
10205         var sbot = stop + ch;
10206         var sright = sleft + c.clientWidth;
10207         /*
10208         Roo.log('GridView.ensureVisible:' +
10209                 ' ctop:' + ctop +
10210                 ' c.clientHeight:' + c.clientHeight +
10211                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10212                 ' stop:' + stop +
10213                 ' cbot:' + cbot +
10214                 ' sbot:' + sbot +
10215                 ' ch:' + ch  
10216                 );
10217         */
10218         if(ctop < stop){
10219             c.scrollTop = ctop;
10220             //Roo.log("set scrolltop to ctop DISABLE?");
10221         }else if(cbot > sbot){
10222             //Roo.log("set scrolltop to cbot-ch");
10223             c.scrollTop = cbot-ch;
10224         }
10225
10226         if(hscroll !== false){
10227             if(cleft < sleft){
10228                 c.scrollLeft = cleft;
10229             }else if(cright > sright){
10230                 c.scrollLeft = cright-c.clientWidth;
10231             }
10232         }
10233
10234         return el;
10235     },
10236     
10237     
10238     insertRow : function(dm, rowIndex, isUpdate){
10239         
10240         if(!isUpdate){
10241             this.fireEvent("beforerowsinserted", this, rowIndex);
10242         }
10243             //var s = this.getScrollState();
10244         var row = this.renderRow(this.cm, this.store, rowIndex);
10245         // insert before rowIndex..
10246         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10247         
10248         var _this = this;
10249                 
10250         if(row.cellObjects.length){
10251             Roo.each(row.cellObjects, function(r){
10252                 _this.renderCellObject(r);
10253             })
10254         }
10255             
10256         if(!isUpdate){
10257             this.fireEvent("rowsinserted", this, rowIndex);
10258             //this.syncRowHeights(firstRow, lastRow);
10259             //this.stripeRows(firstRow);
10260             //this.layout();
10261         }
10262         
10263     },
10264     
10265     
10266     getRowDom : function(rowIndex)
10267     {
10268         var rows = this.el.select('tbody > tr', true).elements;
10269         
10270         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10271         
10272     },
10273     getCellDom : function(rowIndex, colIndex)
10274     {
10275         var row = this.getRowDom(rowIndex);
10276         if (row === false) {
10277             return false;
10278         }
10279         var cols = row.select('td', true).elements;
10280         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10281         
10282     },
10283     
10284     // returns the object tree for a tr..
10285   
10286     
10287     renderRow : function(cm, ds, rowIndex) 
10288     {
10289         var d = ds.getAt(rowIndex);
10290         
10291         var row = {
10292             tag : 'tr',
10293             cls : 'x-row-' + rowIndex,
10294             cn : []
10295         };
10296             
10297         var cellObjects = [];
10298         
10299         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10300             var config = cm.config[i];
10301             
10302             var renderer = cm.getRenderer(i);
10303             var value = '';
10304             var id = false;
10305             
10306             if(typeof(renderer) !== 'undefined'){
10307                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10308             }
10309             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10310             // and are rendered into the cells after the row is rendered - using the id for the element.
10311             
10312             if(typeof(value) === 'object'){
10313                 id = Roo.id();
10314                 cellObjects.push({
10315                     container : id,
10316                     cfg : value 
10317                 })
10318             }
10319             
10320             var rowcfg = {
10321                 record: d,
10322                 rowIndex : rowIndex,
10323                 colIndex : i,
10324                 rowClass : ''
10325             };
10326
10327             this.fireEvent('rowclass', this, rowcfg);
10328             
10329             var td = {
10330                 tag: 'td',
10331                 // this might end up displaying HTML?
10332                 // this is too messy... - better to only do it on columsn you know are going to be too long
10333                 //tooltip : (typeof(value) === 'object') ? '' : value,
10334                 cls : rowcfg.rowClass + ' x-col-' + i,
10335                 style: '',
10336                 html: (typeof(value) === 'object') ? '' : value
10337             };
10338             
10339             if (id) {
10340                 td.id = id;
10341             }
10342             
10343             if(typeof(config.colspan) != 'undefined'){
10344                 td.colspan = config.colspan;
10345             }
10346             
10347             
10348             
10349             if(typeof(config.align) != 'undefined' && config.align.length){
10350                 td.style += ' text-align:' + config.align + ';';
10351             }
10352             if(typeof(config.valign) != 'undefined' && config.valign.length){
10353                 td.style += ' vertical-align:' + config.valign + ';';
10354             }
10355             /*
10356             if(typeof(config.width) != 'undefined'){
10357                 td.style += ' width:' +  config.width + 'px;';
10358             }
10359             */
10360             
10361             if(typeof(config.cursor) != 'undefined'){
10362                 td.style += ' cursor:' +  config.cursor + ';';
10363             }
10364             
10365             if(typeof(config.cls) != 'undefined'){
10366                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10367             }
10368             if (this.responsive) {
10369                 ['xs','sm','md','lg'].map(function(size){
10370                     
10371                     if(typeof(config[size]) == 'undefined'){
10372                         return;
10373                     }
10374                     
10375                     
10376                       
10377                     if (!config[size]) { // 0 = hidden
10378                         // BS 4 '0' is treated as hide that column and below.
10379                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10380                         return;
10381                     }
10382                     
10383                     td.cls += ' col-' + size + '-' + config[size] + (
10384                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10385                     );
10386                      
10387     
10388                 });
10389             }
10390             row.cn.push(td);
10391            
10392         }
10393         
10394         row.cellObjects = cellObjects;
10395         
10396         return row;
10397           
10398     },
10399     
10400     
10401     
10402     onBeforeLoad : function()
10403     {
10404         this.el.unmask(); // if needed.
10405     },
10406      /**
10407      * Remove all rows
10408      */
10409     clear : function()
10410     {
10411         this.el.select('tbody', true).first().dom.innerHTML = '';
10412     },
10413     /**
10414      * Show or hide a row.
10415      * @param {Number} rowIndex to show or hide
10416      * @param {Boolean} state hide
10417      */
10418     setRowVisibility : function(rowIndex, state)
10419     {
10420         var bt = this.bodyEl.dom;
10421         
10422         var rows = this.el.select('tbody > tr', true).elements;
10423         
10424         if(typeof(rows[rowIndex]) == 'undefined'){
10425             return;
10426         }
10427         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10428         
10429     },
10430     
10431     
10432     getSelectionModel : function(){
10433         if(!this.selModel){
10434             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10435         }
10436         return this.selModel;
10437     },
10438     /*
10439      * Render the Roo.bootstrap object from renderder
10440      */
10441     renderCellObject : function(r)
10442     {
10443         var _this = this;
10444         
10445         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10446         
10447         var t = r.cfg.render(r.container);
10448         
10449         if(r.cfg.cn){
10450             Roo.each(r.cfg.cn, function(c){
10451                 var child = {
10452                     container: t.getChildContainer(),
10453                     cfg: c
10454                 };
10455                 _this.renderCellObject(child);
10456             })
10457         }
10458     },
10459     /**
10460      * get the Row Index from a dom element.
10461      * @param {Roo.Element} row The row to look for
10462      * @returns {Number} the row
10463      */
10464     getRowIndex : function(row)
10465     {
10466         var rowIndex = -1;
10467         
10468         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10469             if(el != row){
10470                 return;
10471             }
10472             
10473             rowIndex = index;
10474         });
10475         
10476         return rowIndex;
10477     },
10478     /**
10479      * get the header TH element for columnIndex
10480      * @param {Number} columnIndex
10481      * @returns {Roo.Element}
10482      */
10483     getHeaderIndex: function(colIndex)
10484     {
10485         var cols = this.headEl.select('th', true).elements;
10486         return cols[colIndex]; 
10487     },
10488     /**
10489      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10490      * @param {domElement} cell to look for
10491      * @returns {Number} the column
10492      */
10493     getCellIndex : function(cell)
10494     {
10495         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10496         if(id){
10497             return parseInt(id[1], 10);
10498         }
10499         return 0;
10500     },
10501      /**
10502      * Returns the grid's underlying element = used by panel.Grid
10503      * @return {Element} The element
10504      */
10505     getGridEl : function(){
10506         return this.el;
10507     },
10508      /**
10509      * Forces a resize - used by panel.Grid
10510      * @return {Element} The element
10511      */
10512     autoSize : function()
10513     {
10514         if(this.disableAutoSize) {
10515             return;
10516         }
10517         //var ctr = Roo.get(this.container.dom.parentElement);
10518         var ctr = Roo.get(this.el.dom);
10519         
10520         var thd = this.getGridEl().select('thead',true).first();
10521         var tbd = this.getGridEl().select('tbody', true).first();
10522         var tfd = this.getGridEl().select('tfoot', true).first();
10523         
10524         var cw = ctr.getWidth();
10525         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10526         
10527         if (tbd) {
10528             
10529             tbd.setWidth(ctr.getWidth());
10530             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10531             // this needs fixing for various usage - currently only hydra job advers I think..
10532             //tdb.setHeight(
10533             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10534             //); 
10535             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10536             cw -= barsize;
10537         }
10538         cw = Math.max(cw, this.totalWidth);
10539         this.getGridEl().select('tbody tr',true).setWidth(cw);
10540         this.initCSS();
10541         
10542         // resize 'expandable coloumn?
10543         
10544         return; // we doe not have a view in this design..
10545         
10546     },
10547     onBodyScroll: function()
10548     {
10549         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10550         if(this.headEl){
10551             this.headEl.setStyle({
10552                 'position' : 'relative',
10553                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10554             });
10555         }
10556         
10557         if(this.lazyLoad){
10558             
10559             var scrollHeight = this.bodyEl.dom.scrollHeight;
10560             
10561             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10562             
10563             var height = this.bodyEl.getHeight();
10564             
10565             if(scrollHeight - height == scrollTop) {
10566                 
10567                 var total = this.ds.getTotalCount();
10568                 
10569                 if(this.footer.cursor + this.footer.pageSize < total){
10570                     
10571                     this.footer.ds.load({
10572                         params : {
10573                             start : this.footer.cursor + this.footer.pageSize,
10574                             limit : this.footer.pageSize
10575                         },
10576                         add : true
10577                     });
10578                 }
10579             }
10580             
10581         }
10582     },
10583     onColumnSplitterMoved : function(i, diff)
10584     {
10585         this.userResized = true;
10586         
10587         var cm = this.colModel;
10588         
10589         var w = this.getHeaderIndex(i).getWidth() + diff;
10590         
10591         
10592         cm.setColumnWidth(i, w, true);
10593         this.initCSS();
10594         //var cid = cm.getColumnId(i); << not used in this version?
10595        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10596         
10597         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10598         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10599         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10600 */
10601         //this.updateSplitters();
10602         //this.layout(); << ??
10603         this.fireEvent("columnresize", i, w);
10604     },
10605     onHeaderChange : function()
10606     {
10607         var header = this.renderHeader();
10608         var table = this.el.select('table', true).first();
10609         
10610         this.headEl.remove();
10611         this.headEl = table.createChild(header, this.bodyEl, false);
10612         
10613         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10614             e.on('click', this.sort, this);
10615         }, this);
10616         
10617         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10618             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10619         }
10620         
10621     },
10622     
10623     onHiddenChange : function(colModel, colIndex, hidden)
10624     {
10625         /*
10626         this.cm.setHidden()
10627         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10628         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10629         
10630         this.CSS.updateRule(thSelector, "display", "");
10631         this.CSS.updateRule(tdSelector, "display", "");
10632         
10633         if(hidden){
10634             this.CSS.updateRule(thSelector, "display", "none");
10635             this.CSS.updateRule(tdSelector, "display", "none");
10636         }
10637         */
10638         // onload calls initCSS()
10639         this.onHeaderChange();
10640         this.onLoad();
10641     },
10642     
10643     setColumnWidth: function(col_index, width)
10644     {
10645         // width = "md-2 xs-2..."
10646         if(!this.colModel.config[col_index]) {
10647             return;
10648         }
10649         
10650         var w = width.split(" ");
10651         
10652         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10653         
10654         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10655         
10656         
10657         for(var j = 0; j < w.length; j++) {
10658             
10659             if(!w[j]) {
10660                 continue;
10661             }
10662             
10663             var size_cls = w[j].split("-");
10664             
10665             if(!Number.isInteger(size_cls[1] * 1)) {
10666                 continue;
10667             }
10668             
10669             if(!this.colModel.config[col_index][size_cls[0]]) {
10670                 continue;
10671             }
10672             
10673             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10674                 continue;
10675             }
10676             
10677             h_row[0].classList.replace(
10678                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10679                 "col-"+size_cls[0]+"-"+size_cls[1]
10680             );
10681             
10682             for(var i = 0; i < rows.length; i++) {
10683                 
10684                 var size_cls = w[j].split("-");
10685                 
10686                 if(!Number.isInteger(size_cls[1] * 1)) {
10687                     continue;
10688                 }
10689                 
10690                 if(!this.colModel.config[col_index][size_cls[0]]) {
10691                     continue;
10692                 }
10693                 
10694                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10695                     continue;
10696                 }
10697                 
10698                 rows[i].classList.replace(
10699                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10700                     "col-"+size_cls[0]+"-"+size_cls[1]
10701                 );
10702             }
10703             
10704             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10705         }
10706     }
10707 });
10708
10709 // currently only used to find the split on drag.. 
10710 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10711
10712 /**
10713  * @depricated
10714 */
10715 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10716 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10717 /*
10718  * - LGPL
10719  *
10720  * table cell
10721  * 
10722  */
10723
10724 /**
10725  * @class Roo.bootstrap.TableCell
10726  * @extends Roo.bootstrap.Component
10727  * @children Roo.bootstrap.Component
10728  * @parent Roo.bootstrap.TableRow
10729  * Bootstrap TableCell class
10730  * 
10731  * @cfg {String} html cell contain text
10732  * @cfg {String} cls cell class
10733  * @cfg {String} tag cell tag (td|th) default td
10734  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10735  * @cfg {String} align Aligns the content in a cell
10736  * @cfg {String} axis Categorizes cells
10737  * @cfg {String} bgcolor Specifies the background color of a cell
10738  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10739  * @cfg {Number} colspan Specifies the number of columns a cell should span
10740  * @cfg {String} headers Specifies one or more header cells a cell is related to
10741  * @cfg {Number} height Sets the height of a cell
10742  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10743  * @cfg {Number} rowspan Sets the number of rows a cell should span
10744  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10745  * @cfg {String} valign Vertical aligns the content in a cell
10746  * @cfg {Number} width Specifies the width of a cell
10747  * 
10748  * @constructor
10749  * Create a new TableCell
10750  * @param {Object} config The config object
10751  */
10752
10753 Roo.bootstrap.TableCell = function(config){
10754     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10755 };
10756
10757 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10758     
10759     html: false,
10760     cls: false,
10761     tag: false,
10762     abbr: false,
10763     align: false,
10764     axis: false,
10765     bgcolor: false,
10766     charoff: false,
10767     colspan: false,
10768     headers: false,
10769     height: false,
10770     nowrap: false,
10771     rowspan: false,
10772     scope: false,
10773     valign: false,
10774     width: false,
10775     
10776     
10777     getAutoCreate : function(){
10778         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10779         
10780         cfg = {
10781             tag: 'td'
10782         };
10783         
10784         if(this.tag){
10785             cfg.tag = this.tag;
10786         }
10787         
10788         if (this.html) {
10789             cfg.html=this.html
10790         }
10791         if (this.cls) {
10792             cfg.cls=this.cls
10793         }
10794         if (this.abbr) {
10795             cfg.abbr=this.abbr
10796         }
10797         if (this.align) {
10798             cfg.align=this.align
10799         }
10800         if (this.axis) {
10801             cfg.axis=this.axis
10802         }
10803         if (this.bgcolor) {
10804             cfg.bgcolor=this.bgcolor
10805         }
10806         if (this.charoff) {
10807             cfg.charoff=this.charoff
10808         }
10809         if (this.colspan) {
10810             cfg.colspan=this.colspan
10811         }
10812         if (this.headers) {
10813             cfg.headers=this.headers
10814         }
10815         if (this.height) {
10816             cfg.height=this.height
10817         }
10818         if (this.nowrap) {
10819             cfg.nowrap=this.nowrap
10820         }
10821         if (this.rowspan) {
10822             cfg.rowspan=this.rowspan
10823         }
10824         if (this.scope) {
10825             cfg.scope=this.scope
10826         }
10827         if (this.valign) {
10828             cfg.valign=this.valign
10829         }
10830         if (this.width) {
10831             cfg.width=this.width
10832         }
10833         
10834         
10835         return cfg;
10836     }
10837    
10838 });
10839
10840  
10841
10842  /*
10843  * - LGPL
10844  *
10845  * table row
10846  * 
10847  */
10848
10849 /**
10850  * @class Roo.bootstrap.TableRow
10851  * @extends Roo.bootstrap.Component
10852  * @children Roo.bootstrap.TableCell
10853  * @parent Roo.bootstrap.TableBody
10854  * Bootstrap TableRow class
10855  * @cfg {String} cls row class
10856  * @cfg {String} align Aligns the content in a table row
10857  * @cfg {String} bgcolor Specifies a background color for a table row
10858  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10859  * @cfg {String} valign Vertical aligns the content in a table row
10860  * 
10861  * @constructor
10862  * Create a new TableRow
10863  * @param {Object} config The config object
10864  */
10865
10866 Roo.bootstrap.TableRow = function(config){
10867     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10868 };
10869
10870 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10871     
10872     cls: false,
10873     align: false,
10874     bgcolor: false,
10875     charoff: false,
10876     valign: false,
10877     
10878     getAutoCreate : function(){
10879         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10880         
10881         cfg = {
10882             tag: 'tr'
10883         };
10884             
10885         if(this.cls){
10886             cfg.cls = this.cls;
10887         }
10888         if(this.align){
10889             cfg.align = this.align;
10890         }
10891         if(this.bgcolor){
10892             cfg.bgcolor = this.bgcolor;
10893         }
10894         if(this.charoff){
10895             cfg.charoff = this.charoff;
10896         }
10897         if(this.valign){
10898             cfg.valign = this.valign;
10899         }
10900         
10901         return cfg;
10902     }
10903    
10904 });
10905
10906  
10907
10908  /*
10909  * - LGPL
10910  *
10911  * table body
10912  * 
10913  */
10914
10915 /**
10916  * @class Roo.bootstrap.TableBody
10917  * @extends Roo.bootstrap.Component
10918  * @children Roo.bootstrap.TableRow
10919  * @parent Roo.bootstrap.Table
10920  * Bootstrap TableBody class
10921  * @cfg {String} cls element class
10922  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10923  * @cfg {String} align Aligns the content inside the element
10924  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10925  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10926  * 
10927  * @constructor
10928  * Create a new TableBody
10929  * @param {Object} config The config object
10930  */
10931
10932 Roo.bootstrap.TableBody = function(config){
10933     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10934 };
10935
10936 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10937     
10938     cls: false,
10939     tag: false,
10940     align: false,
10941     charoff: false,
10942     valign: false,
10943     
10944     getAutoCreate : function(){
10945         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10946         
10947         cfg = {
10948             tag: 'tbody'
10949         };
10950             
10951         if (this.cls) {
10952             cfg.cls=this.cls
10953         }
10954         if(this.tag){
10955             cfg.tag = this.tag;
10956         }
10957         
10958         if(this.align){
10959             cfg.align = this.align;
10960         }
10961         if(this.charoff){
10962             cfg.charoff = this.charoff;
10963         }
10964         if(this.valign){
10965             cfg.valign = this.valign;
10966         }
10967         
10968         return cfg;
10969     }
10970     
10971     
10972 //    initEvents : function()
10973 //    {
10974 //        
10975 //        if(!this.store){
10976 //            return;
10977 //        }
10978 //        
10979 //        this.store = Roo.factory(this.store, Roo.data);
10980 //        this.store.on('load', this.onLoad, this);
10981 //        
10982 //        this.store.load();
10983 //        
10984 //    },
10985 //    
10986 //    onLoad: function () 
10987 //    {   
10988 //        this.fireEvent('load', this);
10989 //    }
10990 //    
10991 //   
10992 });
10993
10994  
10995
10996  /*
10997  * Based on:
10998  * Ext JS Library 1.1.1
10999  * Copyright(c) 2006-2007, Ext JS, LLC.
11000  *
11001  * Originally Released Under LGPL - original licence link has changed is not relivant.
11002  *
11003  * Fork - LGPL
11004  * <script type="text/javascript">
11005  */
11006
11007 // as we use this in bootstrap.
11008 Roo.namespace('Roo.form');
11009  /**
11010  * @class Roo.form.Action
11011  * Internal Class used to handle form actions
11012  * @constructor
11013  * @param {Roo.form.BasicForm} el The form element or its id
11014  * @param {Object} config Configuration options
11015  */
11016
11017  
11018  
11019 // define the action interface
11020 Roo.form.Action = function(form, options){
11021     this.form = form;
11022     this.options = options || {};
11023 };
11024 /**
11025  * Client Validation Failed
11026  * @const 
11027  */
11028 Roo.form.Action.CLIENT_INVALID = 'client';
11029 /**
11030  * Server Validation Failed
11031  * @const 
11032  */
11033 Roo.form.Action.SERVER_INVALID = 'server';
11034  /**
11035  * Connect to Server Failed
11036  * @const 
11037  */
11038 Roo.form.Action.CONNECT_FAILURE = 'connect';
11039 /**
11040  * Reading Data from Server Failed
11041  * @const 
11042  */
11043 Roo.form.Action.LOAD_FAILURE = 'load';
11044
11045 Roo.form.Action.prototype = {
11046     type : 'default',
11047     failureType : undefined,
11048     response : undefined,
11049     result : undefined,
11050
11051     // interface method
11052     run : function(options){
11053
11054     },
11055
11056     // interface method
11057     success : function(response){
11058
11059     },
11060
11061     // interface method
11062     handleResponse : function(response){
11063
11064     },
11065
11066     // default connection failure
11067     failure : function(response){
11068         
11069         this.response = response;
11070         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11071         this.form.afterAction(this, false);
11072     },
11073
11074     processResponse : function(response){
11075         this.response = response;
11076         if(!response.responseText){
11077             return true;
11078         }
11079         this.result = this.handleResponse(response);
11080         return this.result;
11081     },
11082
11083     // utility functions used internally
11084     getUrl : function(appendParams){
11085         var url = this.options.url || this.form.url || this.form.el.dom.action;
11086         if(appendParams){
11087             var p = this.getParams();
11088             if(p){
11089                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11090             }
11091         }
11092         return url;
11093     },
11094
11095     getMethod : function(){
11096         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11097     },
11098
11099     getParams : function(){
11100         var bp = this.form.baseParams;
11101         var p = this.options.params;
11102         if(p){
11103             if(typeof p == "object"){
11104                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11105             }else if(typeof p == 'string' && bp){
11106                 p += '&' + Roo.urlEncode(bp);
11107             }
11108         }else if(bp){
11109             p = Roo.urlEncode(bp);
11110         }
11111         return p;
11112     },
11113
11114     createCallback : function(){
11115         return {
11116             success: this.success,
11117             failure: this.failure,
11118             scope: this,
11119             timeout: (this.form.timeout*1000),
11120             upload: this.form.fileUpload ? this.success : undefined
11121         };
11122     }
11123 };
11124
11125 Roo.form.Action.Submit = function(form, options){
11126     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11127 };
11128
11129 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11130     type : 'submit',
11131
11132     haveProgress : false,
11133     uploadComplete : false,
11134     
11135     // uploadProgress indicator.
11136     uploadProgress : function()
11137     {
11138         if (!this.form.progressUrl) {
11139             return;
11140         }
11141         
11142         if (!this.haveProgress) {
11143             Roo.MessageBox.progress("Uploading", "Uploading");
11144         }
11145         if (this.uploadComplete) {
11146            Roo.MessageBox.hide();
11147            return;
11148         }
11149         
11150         this.haveProgress = true;
11151    
11152         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11153         
11154         var c = new Roo.data.Connection();
11155         c.request({
11156             url : this.form.progressUrl,
11157             params: {
11158                 id : uid
11159             },
11160             method: 'GET',
11161             success : function(req){
11162                //console.log(data);
11163                 var rdata = false;
11164                 var edata;
11165                 try  {
11166                    rdata = Roo.decode(req.responseText)
11167                 } catch (e) {
11168                     Roo.log("Invalid data from server..");
11169                     Roo.log(edata);
11170                     return;
11171                 }
11172                 if (!rdata || !rdata.success) {
11173                     Roo.log(rdata);
11174                     Roo.MessageBox.alert(Roo.encode(rdata));
11175                     return;
11176                 }
11177                 var data = rdata.data;
11178                 
11179                 if (this.uploadComplete) {
11180                    Roo.MessageBox.hide();
11181                    return;
11182                 }
11183                    
11184                 if (data){
11185                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11186                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11187                     );
11188                 }
11189                 this.uploadProgress.defer(2000,this);
11190             },
11191        
11192             failure: function(data) {
11193                 Roo.log('progress url failed ');
11194                 Roo.log(data);
11195             },
11196             scope : this
11197         });
11198            
11199     },
11200     
11201     
11202     run : function()
11203     {
11204         // run get Values on the form, so it syncs any secondary forms.
11205         this.form.getValues();
11206         
11207         var o = this.options;
11208         var method = this.getMethod();
11209         var isPost = method == 'POST';
11210         if(o.clientValidation === false || this.form.isValid()){
11211             
11212             if (this.form.progressUrl) {
11213                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11214                     (new Date() * 1) + '' + Math.random());
11215                     
11216             } 
11217             
11218             
11219             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11220                 form:this.form.el.dom,
11221                 url:this.getUrl(!isPost),
11222                 method: method,
11223                 params:isPost ? this.getParams() : null,
11224                 isUpload: this.form.fileUpload,
11225                 formData : this.form.formData
11226             }));
11227             
11228             this.uploadProgress();
11229
11230         }else if (o.clientValidation !== false){ // client validation failed
11231             this.failureType = Roo.form.Action.CLIENT_INVALID;
11232             this.form.afterAction(this, false);
11233         }
11234     },
11235
11236     success : function(response)
11237     {
11238         this.uploadComplete= true;
11239         if (this.haveProgress) {
11240             Roo.MessageBox.hide();
11241         }
11242         
11243         
11244         var result = this.processResponse(response);
11245         if(result === true || result.success){
11246             this.form.afterAction(this, true);
11247             return;
11248         }
11249         if(result.errors){
11250             this.form.markInvalid(result.errors);
11251             this.failureType = Roo.form.Action.SERVER_INVALID;
11252         }
11253         this.form.afterAction(this, false);
11254     },
11255     failure : function(response)
11256     {
11257         this.uploadComplete= true;
11258         if (this.haveProgress) {
11259             Roo.MessageBox.hide();
11260         }
11261         
11262         this.response = response;
11263         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11264         this.form.afterAction(this, false);
11265     },
11266     
11267     handleResponse : function(response){
11268         if(this.form.errorReader){
11269             var rs = this.form.errorReader.read(response);
11270             var errors = [];
11271             if(rs.records){
11272                 for(var i = 0, len = rs.records.length; i < len; i++) {
11273                     var r = rs.records[i];
11274                     errors[i] = r.data;
11275                 }
11276             }
11277             if(errors.length < 1){
11278                 errors = null;
11279             }
11280             return {
11281                 success : rs.success,
11282                 errors : errors
11283             };
11284         }
11285         var ret = false;
11286         try {
11287             ret = Roo.decode(response.responseText);
11288         } catch (e) {
11289             ret = {
11290                 success: false,
11291                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11292                 errors : []
11293             };
11294         }
11295         return ret;
11296         
11297     }
11298 });
11299
11300
11301 Roo.form.Action.Load = function(form, options){
11302     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11303     this.reader = this.form.reader;
11304 };
11305
11306 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11307     type : 'load',
11308
11309     run : function(){
11310         
11311         Roo.Ajax.request(Roo.apply(
11312                 this.createCallback(), {
11313                     method:this.getMethod(),
11314                     url:this.getUrl(false),
11315                     params:this.getParams()
11316         }));
11317     },
11318
11319     success : function(response){
11320         
11321         var result = this.processResponse(response);
11322         if(result === true || !result.success || !result.data){
11323             this.failureType = Roo.form.Action.LOAD_FAILURE;
11324             this.form.afterAction(this, false);
11325             return;
11326         }
11327         this.form.clearInvalid();
11328         this.form.setValues(result.data);
11329         this.form.afterAction(this, true);
11330     },
11331
11332     handleResponse : function(response){
11333         if(this.form.reader){
11334             var rs = this.form.reader.read(response);
11335             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11336             return {
11337                 success : rs.success,
11338                 data : data
11339             };
11340         }
11341         return Roo.decode(response.responseText);
11342     }
11343 });
11344
11345 Roo.form.Action.ACTION_TYPES = {
11346     'load' : Roo.form.Action.Load,
11347     'submit' : Roo.form.Action.Submit
11348 };/*
11349  * - LGPL
11350  *
11351  * form
11352  *
11353  */
11354
11355 /**
11356  * @class Roo.bootstrap.form.Form
11357  * @extends Roo.bootstrap.Component
11358  * @children Roo.bootstrap.Component
11359  * Bootstrap Form class
11360  * @cfg {String} method  GET | POST (default POST)
11361  * @cfg {String} labelAlign top | left (default top)
11362  * @cfg {String} align left  | right - for navbars
11363  * @cfg {Boolean} loadMask load mask when submit (default true)
11364
11365  *
11366  * @constructor
11367  * Create a new Form
11368  * @param {Object} config The config object
11369  */
11370
11371
11372 Roo.bootstrap.form.Form = function(config){
11373     
11374     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11375     
11376     Roo.bootstrap.form.Form.popover.apply();
11377     
11378     this.addEvents({
11379         /**
11380          * @event clientvalidation
11381          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11382          * @param {Form} this
11383          * @param {Boolean} valid true if the form has passed client-side validation
11384          */
11385         clientvalidation: true,
11386         /**
11387          * @event beforeaction
11388          * Fires before any action is performed. Return false to cancel the action.
11389          * @param {Form} this
11390          * @param {Action} action The action to be performed
11391          */
11392         beforeaction: true,
11393         /**
11394          * @event actionfailed
11395          * Fires when an action fails.
11396          * @param {Form} this
11397          * @param {Action} action The action that failed
11398          */
11399         actionfailed : true,
11400         /**
11401          * @event actioncomplete
11402          * Fires when an action is completed.
11403          * @param {Form} this
11404          * @param {Action} action The action that completed
11405          */
11406         actioncomplete : true
11407     });
11408 };
11409
11410 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11411
11412      /**
11413      * @cfg {String} method
11414      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11415      */
11416     method : 'POST',
11417     /**
11418      * @cfg {String} url
11419      * The URL to use for form actions if one isn't supplied in the action options.
11420      */
11421     /**
11422      * @cfg {Boolean} fileUpload
11423      * Set to true if this form is a file upload.
11424      */
11425
11426     /**
11427      * @cfg {Object} baseParams
11428      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11429      */
11430
11431     /**
11432      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11433      */
11434     timeout: 30,
11435     /**
11436      * @cfg {Sting} align (left|right) for navbar forms
11437      */
11438     align : 'left',
11439
11440     // private
11441     activeAction : null,
11442
11443     /**
11444      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11445      * element by passing it or its id or mask the form itself by passing in true.
11446      * @type Mixed
11447      */
11448     waitMsgTarget : false,
11449
11450     loadMask : true,
11451     
11452     /**
11453      * @cfg {Boolean} errorMask (true|false) default false
11454      */
11455     errorMask : false,
11456     
11457     /**
11458      * @cfg {Number} maskOffset Default 100
11459      */
11460     maskOffset : 100,
11461     
11462     /**
11463      * @cfg {Boolean} maskBody
11464      */
11465     maskBody : false,
11466
11467     getAutoCreate : function(){
11468
11469         var cfg = {
11470             tag: 'form',
11471             method : this.method || 'POST',
11472             id : this.id || Roo.id(),
11473             cls : ''
11474         };
11475         if (this.parent().xtype.match(/^Nav/)) {
11476             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11477
11478         }
11479
11480         if (this.labelAlign == 'left' ) {
11481             cfg.cls += ' form-horizontal';
11482         }
11483
11484
11485         return cfg;
11486     },
11487     initEvents : function()
11488     {
11489         this.el.on('submit', this.onSubmit, this);
11490         // this was added as random key presses on the form where triggering form submit.
11491         this.el.on('keypress', function(e) {
11492             if (e.getCharCode() != 13) {
11493                 return true;
11494             }
11495             // we might need to allow it for textareas.. and some other items.
11496             // check e.getTarget().
11497
11498             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11499                 return true;
11500             }
11501
11502             Roo.log("keypress blocked");
11503
11504             e.preventDefault();
11505             return false;
11506         });
11507         
11508     },
11509     // private
11510     onSubmit : function(e){
11511         e.stopEvent();
11512     },
11513
11514      /**
11515      * Returns true if client-side validation on the form is successful.
11516      * @return Boolean
11517      */
11518     isValid : function(){
11519         var items = this.getItems();
11520         var valid = true;
11521         var target = false;
11522         
11523         items.each(function(f){
11524             
11525             if(f.validate()){
11526                 return;
11527             }
11528             
11529             Roo.log('invalid field: ' + f.name);
11530             
11531             valid = false;
11532
11533             if(!target && f.el.isVisible(true)){
11534                 target = f;
11535             }
11536            
11537         });
11538         
11539         if(this.errorMask && !valid){
11540             Roo.bootstrap.form.Form.popover.mask(this, target);
11541         }
11542         
11543         return valid;
11544     },
11545     
11546     /**
11547      * Returns true if any fields in this form have changed since their original load.
11548      * @return Boolean
11549      */
11550     isDirty : function(){
11551         var dirty = false;
11552         var items = this.getItems();
11553         items.each(function(f){
11554            if(f.isDirty()){
11555                dirty = true;
11556                return false;
11557            }
11558            return true;
11559         });
11560         return dirty;
11561     },
11562      /**
11563      * Performs a predefined action (submit or load) or custom actions you define on this form.
11564      * @param {String} actionName The name of the action type
11565      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11566      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11567      * accept other config options):
11568      * <pre>
11569 Property          Type             Description
11570 ----------------  ---------------  ----------------------------------------------------------------------------------
11571 url               String           The url for the action (defaults to the form's url)
11572 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11573 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11574 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11575                                    validate the form on the client (defaults to false)
11576      * </pre>
11577      * @return {BasicForm} this
11578      */
11579     doAction : function(action, options){
11580         if(typeof action == 'string'){
11581             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11582         }
11583         if(this.fireEvent('beforeaction', this, action) !== false){
11584             this.beforeAction(action);
11585             action.run.defer(100, action);
11586         }
11587         return this;
11588     },
11589
11590     // private
11591     beforeAction : function(action){
11592         var o = action.options;
11593         
11594         if(this.loadMask){
11595             
11596             if(this.maskBody){
11597                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11598             } else {
11599                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11600             }
11601         }
11602         // not really supported yet.. ??
11603
11604         //if(this.waitMsgTarget === true){
11605         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11606         //}else if(this.waitMsgTarget){
11607         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11608         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11609         //}else {
11610         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11611        // }
11612
11613     },
11614
11615     // private
11616     afterAction : function(action, success){
11617         this.activeAction = null;
11618         var o = action.options;
11619
11620         if(this.loadMask){
11621             
11622             if(this.maskBody){
11623                 Roo.get(document.body).unmask();
11624             } else {
11625                 this.el.unmask();
11626             }
11627         }
11628         
11629         //if(this.waitMsgTarget === true){
11630 //            this.el.unmask();
11631         //}else if(this.waitMsgTarget){
11632         //    this.waitMsgTarget.unmask();
11633         //}else{
11634         //    Roo.MessageBox.updateProgress(1);
11635         //    Roo.MessageBox.hide();
11636        // }
11637         //
11638         if(success){
11639             if(o.reset){
11640                 this.reset();
11641             }
11642             Roo.callback(o.success, o.scope, [this, action]);
11643             this.fireEvent('actioncomplete', this, action);
11644
11645         }else{
11646
11647             // failure condition..
11648             // we have a scenario where updates need confirming.
11649             // eg. if a locking scenario exists..
11650             // we look for { errors : { needs_confirm : true }} in the response.
11651             if (
11652                 (typeof(action.result) != 'undefined')  &&
11653                 (typeof(action.result.errors) != 'undefined')  &&
11654                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11655            ){
11656                 var _t = this;
11657                 Roo.log("not supported yet");
11658                  /*
11659
11660                 Roo.MessageBox.confirm(
11661                     "Change requires confirmation",
11662                     action.result.errorMsg,
11663                     function(r) {
11664                         if (r != 'yes') {
11665                             return;
11666                         }
11667                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11668                     }
11669
11670                 );
11671                 */
11672
11673
11674                 return;
11675             }
11676
11677             Roo.callback(o.failure, o.scope, [this, action]);
11678             // show an error message if no failed handler is set..
11679             if (!this.hasListener('actionfailed')) {
11680                 Roo.log("need to add dialog support");
11681                 /*
11682                 Roo.MessageBox.alert("Error",
11683                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11684                         action.result.errorMsg :
11685                         "Saving Failed, please check your entries or try again"
11686                 );
11687                 */
11688             }
11689
11690             this.fireEvent('actionfailed', this, action);
11691         }
11692
11693     },
11694     /**
11695      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11696      * @param {String} id The value to search for
11697      * @return Field
11698      */
11699     findField : function(id){
11700         var items = this.getItems();
11701         var field = items.get(id);
11702         if(!field){
11703              items.each(function(f){
11704                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11705                     field = f;
11706                     return false;
11707                 }
11708                 return true;
11709             });
11710         }
11711         return field || null;
11712     },
11713      /**
11714      * Mark fields in this form invalid in bulk.
11715      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11716      * @return {BasicForm} this
11717      */
11718     markInvalid : function(errors){
11719         if(errors instanceof Array){
11720             for(var i = 0, len = errors.length; i < len; i++){
11721                 var fieldError = errors[i];
11722                 var f = this.findField(fieldError.id);
11723                 if(f){
11724                     f.markInvalid(fieldError.msg);
11725                 }
11726             }
11727         }else{
11728             var field, id;
11729             for(id in errors){
11730                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11731                     field.markInvalid(errors[id]);
11732                 }
11733             }
11734         }
11735         //Roo.each(this.childForms || [], function (f) {
11736         //    f.markInvalid(errors);
11737         //});
11738
11739         return this;
11740     },
11741
11742     /**
11743      * Set values for fields in this form in bulk.
11744      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11745      * @return {BasicForm} this
11746      */
11747     setValues : function(values){
11748         if(values instanceof Array){ // array of objects
11749             for(var i = 0, len = values.length; i < len; i++){
11750                 var v = values[i];
11751                 var f = this.findField(v.id);
11752                 if(f){
11753                     f.setValue(v.value);
11754                     if(this.trackResetOnLoad){
11755                         f.originalValue = f.getValue();
11756                     }
11757                 }
11758             }
11759         }else{ // object hash
11760             var field, id;
11761             for(id in values){
11762                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11763
11764                     if (field.setFromData &&
11765                         field.valueField &&
11766                         field.displayField &&
11767                         // combos' with local stores can
11768                         // be queried via setValue()
11769                         // to set their value..
11770                         (field.store && !field.store.isLocal)
11771                         ) {
11772                         // it's a combo
11773                         var sd = { };
11774                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11775                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11776                         field.setFromData(sd);
11777
11778                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11779                         
11780                         field.setFromData(values);
11781                         
11782                     } else {
11783                         field.setValue(values[id]);
11784                     }
11785
11786
11787                     if(this.trackResetOnLoad){
11788                         field.originalValue = field.getValue();
11789                     }
11790                 }
11791             }
11792         }
11793
11794         //Roo.each(this.childForms || [], function (f) {
11795         //    f.setValues(values);
11796         //});
11797
11798         return this;
11799     },
11800
11801     /**
11802      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11803      * they are returned as an array.
11804      * @param {Boolean} asString
11805      * @return {Object}
11806      */
11807     getValues : function(asString){
11808         //if (this.childForms) {
11809             // copy values from the child forms
11810         //    Roo.each(this.childForms, function (f) {
11811         //        this.setValues(f.getValues());
11812         //    }, this);
11813         //}
11814
11815
11816
11817         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11818         if(asString === true){
11819             return fs;
11820         }
11821         return Roo.urlDecode(fs);
11822     },
11823
11824     /**
11825      * Returns the fields in this form as an object with key/value pairs.
11826      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11827      * @return {Object}
11828      */
11829     getFieldValues : function(with_hidden)
11830     {
11831         var items = this.getItems();
11832         var ret = {};
11833         items.each(function(f){
11834             
11835             if (!f.getName()) {
11836                 return;
11837             }
11838             
11839             var v = f.getValue();
11840             
11841             if (f.inputType =='radio') {
11842                 if (typeof(ret[f.getName()]) == 'undefined') {
11843                     ret[f.getName()] = ''; // empty..
11844                 }
11845
11846                 if (!f.el.dom.checked) {
11847                     return;
11848
11849                 }
11850                 v = f.el.dom.value;
11851
11852             }
11853             
11854             if(f.xtype == 'MoneyField'){
11855                 ret[f.currencyName] = f.getCurrency();
11856             }
11857
11858             // not sure if this supported any more..
11859             if ((typeof(v) == 'object') && f.getRawValue) {
11860                 v = f.getRawValue() ; // dates..
11861             }
11862             // combo boxes where name != hiddenName...
11863             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11864                 ret[f.name] = f.getRawValue();
11865             }
11866             ret[f.getName()] = v;
11867         });
11868
11869         return ret;
11870     },
11871
11872     /**
11873      * Clears all invalid messages in this form.
11874      * @return {BasicForm} this
11875      */
11876     clearInvalid : function(){
11877         var items = this.getItems();
11878
11879         items.each(function(f){
11880            f.clearInvalid();
11881         });
11882
11883         return this;
11884     },
11885
11886     /**
11887      * Resets this form.
11888      * @return {BasicForm} this
11889      */
11890     reset : function(){
11891         var items = this.getItems();
11892         items.each(function(f){
11893             f.reset();
11894         });
11895
11896         Roo.each(this.childForms || [], function (f) {
11897             f.reset();
11898         });
11899
11900
11901         return this;
11902     },
11903     
11904     getItems : function()
11905     {
11906         var r=new Roo.util.MixedCollection(false, function(o){
11907             return o.id || (o.id = Roo.id());
11908         });
11909         var iter = function(el) {
11910             if (el.inputEl) {
11911                 r.add(el);
11912             }
11913             if (!el.items) {
11914                 return;
11915             }
11916             Roo.each(el.items,function(e) {
11917                 iter(e);
11918             });
11919         };
11920
11921         iter(this);
11922         return r;
11923     },
11924     
11925     hideFields : function(items)
11926     {
11927         Roo.each(items, function(i){
11928             
11929             var f = this.findField(i);
11930             
11931             if(!f){
11932                 return;
11933             }
11934             
11935             f.hide();
11936             
11937         }, this);
11938     },
11939     
11940     showFields : function(items)
11941     {
11942         Roo.each(items, function(i){
11943             
11944             var f = this.findField(i);
11945             
11946             if(!f){
11947                 return;
11948             }
11949             
11950             f.show();
11951             
11952         }, this);
11953     }
11954
11955 });
11956
11957 Roo.apply(Roo.bootstrap.form.Form, {
11958     
11959     popover : {
11960         
11961         padding : 5,
11962         
11963         isApplied : false,
11964         
11965         isMasked : false,
11966         
11967         form : false,
11968         
11969         target : false,
11970         
11971         toolTip : false,
11972         
11973         intervalID : false,
11974         
11975         maskEl : false,
11976         
11977         apply : function()
11978         {
11979             if(this.isApplied){
11980                 return;
11981             }
11982             
11983             this.maskEl = {
11984                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11985                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11986                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11987                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11988             };
11989             
11990             this.maskEl.top.enableDisplayMode("block");
11991             this.maskEl.left.enableDisplayMode("block");
11992             this.maskEl.bottom.enableDisplayMode("block");
11993             this.maskEl.right.enableDisplayMode("block");
11994             
11995             this.toolTip = new Roo.bootstrap.Tooltip({
11996                 cls : 'roo-form-error-popover',
11997                 alignment : {
11998                     'left' : ['r-l', [-2,0], 'right'],
11999                     'right' : ['l-r', [2,0], 'left'],
12000                     'bottom' : ['tl-bl', [0,2], 'top'],
12001                     'top' : [ 'bl-tl', [0,-2], 'bottom']
12002                 }
12003             });
12004             
12005             this.toolTip.render(Roo.get(document.body));
12006
12007             this.toolTip.el.enableDisplayMode("block");
12008             
12009             Roo.get(document.body).on('click', function(){
12010                 this.unmask();
12011             }, this);
12012             
12013             Roo.get(document.body).on('touchstart', function(){
12014                 this.unmask();
12015             }, this);
12016             
12017             this.isApplied = true
12018         },
12019         
12020         mask : function(form, target)
12021         {
12022             this.form = form;
12023             
12024             this.target = target;
12025             
12026             if(!this.form.errorMask || !target.el){
12027                 return;
12028             }
12029             
12030             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12031             
12032             Roo.log(scrollable);
12033             
12034             var ot = this.target.el.calcOffsetsTo(scrollable);
12035             
12036             var scrollTo = ot[1] - this.form.maskOffset;
12037             
12038             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12039             
12040             scrollable.scrollTo('top', scrollTo);
12041             
12042             var box = this.target.el.getBox();
12043             Roo.log(box);
12044             var zIndex = Roo.bootstrap.Modal.zIndex++;
12045
12046             
12047             this.maskEl.top.setStyle('position', 'absolute');
12048             this.maskEl.top.setStyle('z-index', zIndex);
12049             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12050             this.maskEl.top.setLeft(0);
12051             this.maskEl.top.setTop(0);
12052             this.maskEl.top.show();
12053             
12054             this.maskEl.left.setStyle('position', 'absolute');
12055             this.maskEl.left.setStyle('z-index', zIndex);
12056             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12057             this.maskEl.left.setLeft(0);
12058             this.maskEl.left.setTop(box.y - this.padding);
12059             this.maskEl.left.show();
12060
12061             this.maskEl.bottom.setStyle('position', 'absolute');
12062             this.maskEl.bottom.setStyle('z-index', zIndex);
12063             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12064             this.maskEl.bottom.setLeft(0);
12065             this.maskEl.bottom.setTop(box.bottom + this.padding);
12066             this.maskEl.bottom.show();
12067
12068             this.maskEl.right.setStyle('position', 'absolute');
12069             this.maskEl.right.setStyle('z-index', zIndex);
12070             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12071             this.maskEl.right.setLeft(box.right + this.padding);
12072             this.maskEl.right.setTop(box.y - this.padding);
12073             this.maskEl.right.show();
12074
12075             this.toolTip.bindEl = this.target.el;
12076
12077             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12078
12079             var tip = this.target.blankText;
12080
12081             if(this.target.getValue() !== '' ) {
12082                 
12083                 if (this.target.invalidText.length) {
12084                     tip = this.target.invalidText;
12085                 } else if (this.target.regexText.length){
12086                     tip = this.target.regexText;
12087                 }
12088             }
12089
12090             this.toolTip.show(tip);
12091
12092             this.intervalID = window.setInterval(function() {
12093                 Roo.bootstrap.form.Form.popover.unmask();
12094             }, 10000);
12095
12096             window.onwheel = function(){ return false;};
12097             
12098             (function(){ this.isMasked = true; }).defer(500, this);
12099             
12100         },
12101         
12102         unmask : function()
12103         {
12104             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12105                 return;
12106             }
12107             
12108             this.maskEl.top.setStyle('position', 'absolute');
12109             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12110             this.maskEl.top.hide();
12111
12112             this.maskEl.left.setStyle('position', 'absolute');
12113             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12114             this.maskEl.left.hide();
12115
12116             this.maskEl.bottom.setStyle('position', 'absolute');
12117             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12118             this.maskEl.bottom.hide();
12119
12120             this.maskEl.right.setStyle('position', 'absolute');
12121             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12122             this.maskEl.right.hide();
12123             
12124             this.toolTip.hide();
12125             
12126             this.toolTip.el.hide();
12127             
12128             window.onwheel = function(){ return true;};
12129             
12130             if(this.intervalID){
12131                 window.clearInterval(this.intervalID);
12132                 this.intervalID = false;
12133             }
12134             
12135             this.isMasked = false;
12136             
12137         }
12138         
12139     }
12140     
12141 });
12142
12143 /*
12144  * Based on:
12145  * Ext JS Library 1.1.1
12146  * Copyright(c) 2006-2007, Ext JS, LLC.
12147  *
12148  * Originally Released Under LGPL - original licence link has changed is not relivant.
12149  *
12150  * Fork - LGPL
12151  * <script type="text/javascript">
12152  */
12153 /**
12154  * @class Roo.form.VTypes
12155  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12156  * @static
12157  */
12158 Roo.form.VTypes = function(){
12159     // closure these in so they are only created once.
12160     var alpha = /^[a-zA-Z_]+$/;
12161     var alphanum = /^[a-zA-Z0-9_]+$/;
12162     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12163     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12164
12165     // All these messages and functions are configurable
12166     return {
12167         /**
12168          * The function used to validate email addresses
12169          * @param {String} value The email address
12170          */
12171         'email' : function(v){
12172             return email.test(v);
12173         },
12174         /**
12175          * The error text to display when the email validation function returns false
12176          * @type String
12177          */
12178         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12179         /**
12180          * The keystroke filter mask to be applied on email input
12181          * @type RegExp
12182          */
12183         'emailMask' : /[a-z0-9_\.\-@]/i,
12184
12185         /**
12186          * The function used to validate URLs
12187          * @param {String} value The URL
12188          */
12189         'url' : function(v){
12190             return url.test(v);
12191         },
12192         /**
12193          * The error text to display when the url validation function returns false
12194          * @type String
12195          */
12196         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12197         
12198         /**
12199          * The function used to validate alpha values
12200          * @param {String} value The value
12201          */
12202         'alpha' : function(v){
12203             return alpha.test(v);
12204         },
12205         /**
12206          * The error text to display when the alpha validation function returns false
12207          * @type String
12208          */
12209         'alphaText' : 'This field should only contain letters and _',
12210         /**
12211          * The keystroke filter mask to be applied on alpha input
12212          * @type RegExp
12213          */
12214         'alphaMask' : /[a-z_]/i,
12215
12216         /**
12217          * The function used to validate alphanumeric values
12218          * @param {String} value The value
12219          */
12220         'alphanum' : function(v){
12221             return alphanum.test(v);
12222         },
12223         /**
12224          * The error text to display when the alphanumeric validation function returns false
12225          * @type String
12226          */
12227         'alphanumText' : 'This field should only contain letters, numbers and _',
12228         /**
12229          * The keystroke filter mask to be applied on alphanumeric input
12230          * @type RegExp
12231          */
12232         'alphanumMask' : /[a-z0-9_]/i
12233     };
12234 }();/*
12235  * - LGPL
12236  *
12237  * Input
12238  * 
12239  */
12240
12241 /**
12242  * @class Roo.bootstrap.form.Input
12243  * @extends Roo.bootstrap.Component
12244  * Bootstrap Input class
12245  * @cfg {Boolean} disabled is it disabled
12246  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12247  * @cfg {String} name name of the input
12248  * @cfg {string} fieldLabel - the label associated
12249  * @cfg {string} placeholder - placeholder to put in text.
12250  * @cfg {string} before - input group add on before
12251  * @cfg {string} after - input group add on after
12252  * @cfg {string} size - (lg|sm) or leave empty..
12253  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12254  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12255  * @cfg {Number} md colspan out of 12 for computer-sized screens
12256  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12257  * @cfg {string} value default value of the input
12258  * @cfg {Number} labelWidth set the width of label 
12259  * @cfg {Number} labellg set the width of label (1-12)
12260  * @cfg {Number} labelmd set the width of label (1-12)
12261  * @cfg {Number} labelsm set the width of label (1-12)
12262  * @cfg {Number} labelxs set the width of label (1-12)
12263  * @cfg {String} labelAlign (top|left)
12264  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12265  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12266  * @cfg {String} indicatorpos (left|right) default left
12267  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12268  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12269  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12270  * @cfg {Roo.bootstrap.Button} before Button to show before
12271  * @cfg {Roo.bootstrap.Button} afterButton to show before
12272  * @cfg {String} align (left|center|right) Default left
12273  * @cfg {Boolean} forceFeedback (true|false) Default false
12274  * 
12275  * @constructor
12276  * Create a new Input
12277  * @param {Object} config The config object
12278  */
12279
12280 Roo.bootstrap.form.Input = function(config){
12281     
12282     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12283     
12284     this.addEvents({
12285         /**
12286          * @event focus
12287          * Fires when this field receives input focus.
12288          * @param {Roo.form.Field} this
12289          */
12290         focus : true,
12291         /**
12292          * @event blur
12293          * Fires when this field loses input focus.
12294          * @param {Roo.form.Field} this
12295          */
12296         blur : true,
12297         /**
12298          * @event specialkey
12299          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12300          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12301          * @param {Roo.form.Field} this
12302          * @param {Roo.EventObject} e The event object
12303          */
12304         specialkey : true,
12305         /**
12306          * @event change
12307          * Fires just before the field blurs if the field value has changed.
12308          * @param {Roo.form.Field} this
12309          * @param {Mixed} newValue The new value
12310          * @param {Mixed} oldValue The original value
12311          */
12312         change : true,
12313         /**
12314          * @event invalid
12315          * Fires after the field has been marked as invalid.
12316          * @param {Roo.form.Field} this
12317          * @param {String} msg The validation message
12318          */
12319         invalid : true,
12320         /**
12321          * @event valid
12322          * Fires after the field has been validated with no errors.
12323          * @param {Roo.form.Field} this
12324          */
12325         valid : true,
12326          /**
12327          * @event keyup
12328          * Fires after the key up
12329          * @param {Roo.form.Field} this
12330          * @param {Roo.EventObject}  e The event Object
12331          */
12332         keyup : true,
12333         /**
12334          * @event paste
12335          * Fires after the user pastes into input
12336          * @param {Roo.form.Field} this
12337          * @param {Roo.EventObject}  e The event Object
12338          */
12339         paste : true
12340     });
12341 };
12342
12343 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12344      /**
12345      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12346       automatic validation (defaults to "keyup").
12347      */
12348     validationEvent : "keyup",
12349      /**
12350      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12351      */
12352     validateOnBlur : true,
12353     /**
12354      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12355      */
12356     validationDelay : 250,
12357      /**
12358      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12359      */
12360     focusClass : "x-form-focus",  // not needed???
12361     
12362        
12363     /**
12364      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12365      */
12366     invalidClass : "has-warning",
12367     
12368     /**
12369      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12370      */
12371     validClass : "has-success",
12372     
12373     /**
12374      * @cfg {Boolean} hasFeedback (true|false) default true
12375      */
12376     hasFeedback : true,
12377     
12378     /**
12379      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12380      */
12381     invalidFeedbackClass : "glyphicon-warning-sign",
12382     
12383     /**
12384      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12385      */
12386     validFeedbackClass : "glyphicon-ok",
12387     
12388     /**
12389      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12390      */
12391     selectOnFocus : false,
12392     
12393      /**
12394      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12395      */
12396     maskRe : null,
12397        /**
12398      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12399      */
12400     vtype : null,
12401     
12402       /**
12403      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12404      */
12405     disableKeyFilter : false,
12406     
12407        /**
12408      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12409      */
12410     disabled : false,
12411      /**
12412      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12413      */
12414     allowBlank : true,
12415     /**
12416      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12417      */
12418     blankText : "Please complete this mandatory field",
12419     
12420      /**
12421      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12422      */
12423     minLength : 0,
12424     /**
12425      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12426      */
12427     maxLength : Number.MAX_VALUE,
12428     /**
12429      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12430      */
12431     minLengthText : "The minimum length for this field is {0}",
12432     /**
12433      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12434      */
12435     maxLengthText : "The maximum length for this field is {0}",
12436   
12437     
12438     /**
12439      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12440      * If available, this function will be called only after the basic validators all return true, and will be passed the
12441      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12442      */
12443     validator : null,
12444     /**
12445      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12446      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12447      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12448      */
12449     regex : null,
12450     /**
12451      * @cfg {String} regexText -- Depricated - use Invalid Text
12452      */
12453     regexText : "",
12454     
12455     /**
12456      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12457      */
12458     invalidText : "",
12459     
12460     
12461     
12462     autocomplete: false,
12463     
12464     
12465     fieldLabel : '',
12466     inputType : 'text',
12467     
12468     name : false,
12469     placeholder: false,
12470     before : false,
12471     after : false,
12472     size : false,
12473     hasFocus : false,
12474     preventMark: false,
12475     isFormField : true,
12476     value : '',
12477     labelWidth : 2,
12478     labelAlign : false,
12479     readOnly : false,
12480     align : false,
12481     formatedValue : false,
12482     forceFeedback : false,
12483     
12484     indicatorpos : 'left',
12485     
12486     labellg : 0,
12487     labelmd : 0,
12488     labelsm : 0,
12489     labelxs : 0,
12490     
12491     capture : '',
12492     accept : '',
12493     
12494     parentLabelAlign : function()
12495     {
12496         var parent = this;
12497         while (parent.parent()) {
12498             parent = parent.parent();
12499             if (typeof(parent.labelAlign) !='undefined') {
12500                 return parent.labelAlign;
12501             }
12502         }
12503         return 'left';
12504         
12505     },
12506     
12507     getAutoCreate : function()
12508     {
12509         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12510         
12511         var id = Roo.id();
12512         
12513         var cfg = {};
12514         
12515         if(this.inputType != 'hidden'){
12516             cfg.cls = 'form-group' //input-group
12517         }
12518         
12519         var input =  {
12520             tag: 'input',
12521             id : id,
12522             type : this.inputType,
12523             value : this.value,
12524             cls : 'form-control',
12525             placeholder : this.placeholder || '',
12526             autocomplete : this.autocomplete || 'new-password'
12527         };
12528         if (this.inputType == 'file') {
12529             input.style = 'overflow:hidden'; // why not in CSS?
12530         }
12531         
12532         if(this.capture.length){
12533             input.capture = this.capture;
12534         }
12535         
12536         if(this.accept.length){
12537             input.accept = this.accept + "/*";
12538         }
12539         
12540         if(this.align){
12541             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12542         }
12543         
12544         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12545             input.maxLength = this.maxLength;
12546         }
12547         
12548         if (this.disabled) {
12549             input.disabled=true;
12550         }
12551         
12552         if (this.readOnly) {
12553             input.readonly=true;
12554         }
12555         
12556         if (this.name) {
12557             input.name = this.name;
12558         }
12559         
12560         if (this.size) {
12561             input.cls += ' input-' + this.size;
12562         }
12563         
12564         var settings=this;
12565         ['xs','sm','md','lg'].map(function(size){
12566             if (settings[size]) {
12567                 cfg.cls += ' col-' + size + '-' + settings[size];
12568             }
12569         });
12570         
12571         var inputblock = input;
12572         
12573         var feedback = {
12574             tag: 'span',
12575             cls: 'glyphicon form-control-feedback'
12576         };
12577             
12578         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12579             
12580             inputblock = {
12581                 cls : 'has-feedback',
12582                 cn :  [
12583                     input,
12584                     feedback
12585                 ] 
12586             };  
12587         }
12588         
12589         if (this.before || this.after) {
12590             
12591             inputblock = {
12592                 cls : 'input-group',
12593                 cn :  [] 
12594             };
12595             
12596             if (this.before && typeof(this.before) == 'string') {
12597                 
12598                 inputblock.cn.push({
12599                     tag :'span',
12600                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12601                     html : this.before
12602                 });
12603             }
12604             if (this.before && typeof(this.before) == 'object') {
12605                 this.before = Roo.factory(this.before);
12606                 
12607                 inputblock.cn.push({
12608                     tag :'span',
12609                     cls : 'roo-input-before input-group-prepend   input-group-' +
12610                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12611                 });
12612             }
12613             
12614             inputblock.cn.push(input);
12615             
12616             if (this.after && typeof(this.after) == 'string') {
12617                 inputblock.cn.push({
12618                     tag :'span',
12619                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12620                     html : this.after
12621                 });
12622             }
12623             if (this.after && typeof(this.after) == 'object') {
12624                 this.after = Roo.factory(this.after);
12625                 
12626                 inputblock.cn.push({
12627                     tag :'span',
12628                     cls : 'roo-input-after input-group-append  input-group-' +
12629                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12630                 });
12631             }
12632             
12633             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12634                 inputblock.cls += ' has-feedback';
12635                 inputblock.cn.push(feedback);
12636             }
12637         };
12638         var indicator = {
12639             tag : 'i',
12640             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12641             tooltip : 'This field is required'
12642         };
12643         if (this.allowBlank ) {
12644             indicator.style = this.allowBlank ? ' display:none' : '';
12645         }
12646         if (align ==='left' && this.fieldLabel.length) {
12647             
12648             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12649             
12650             cfg.cn = [
12651                 indicator,
12652                 {
12653                     tag: 'label',
12654                     'for' :  id,
12655                     cls : 'control-label col-form-label',
12656                     html : this.fieldLabel
12657
12658                 },
12659                 {
12660                     cls : "", 
12661                     cn: [
12662                         inputblock
12663                     ]
12664                 }
12665             ];
12666             
12667             var labelCfg = cfg.cn[1];
12668             var contentCfg = cfg.cn[2];
12669             
12670             if(this.indicatorpos == 'right'){
12671                 cfg.cn = [
12672                     {
12673                         tag: 'label',
12674                         'for' :  id,
12675                         cls : 'control-label col-form-label',
12676                         cn : [
12677                             {
12678                                 tag : 'span',
12679                                 html : this.fieldLabel
12680                             },
12681                             indicator
12682                         ]
12683                     },
12684                     {
12685                         cls : "",
12686                         cn: [
12687                             inputblock
12688                         ]
12689                     }
12690
12691                 ];
12692                 
12693                 labelCfg = cfg.cn[0];
12694                 contentCfg = cfg.cn[1];
12695             
12696             }
12697             
12698             if(this.labelWidth > 12){
12699                 labelCfg.style = "width: " + this.labelWidth + 'px';
12700             }
12701             
12702             if(this.labelWidth < 13 && this.labelmd == 0){
12703                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12704             }
12705             
12706             if(this.labellg > 0){
12707                 labelCfg.cls += ' col-lg-' + this.labellg;
12708                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12709             }
12710             
12711             if(this.labelmd > 0){
12712                 labelCfg.cls += ' col-md-' + this.labelmd;
12713                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12714             }
12715             
12716             if(this.labelsm > 0){
12717                 labelCfg.cls += ' col-sm-' + this.labelsm;
12718                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12719             }
12720             
12721             if(this.labelxs > 0){
12722                 labelCfg.cls += ' col-xs-' + this.labelxs;
12723                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12724             }
12725             
12726             
12727         } else if ( this.fieldLabel.length) {
12728                 
12729             
12730             
12731             cfg.cn = [
12732                 {
12733                     tag : 'i',
12734                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12735                     tooltip : 'This field is required',
12736                     style : this.allowBlank ? ' display:none' : '' 
12737                 },
12738                 {
12739                     tag: 'label',
12740                    //cls : 'input-group-addon',
12741                     html : this.fieldLabel
12742
12743                 },
12744
12745                inputblock
12746
12747            ];
12748            
12749            if(this.indicatorpos == 'right'){
12750        
12751                 cfg.cn = [
12752                     {
12753                         tag: 'label',
12754                        //cls : 'input-group-addon',
12755                         html : this.fieldLabel
12756
12757                     },
12758                     {
12759                         tag : 'i',
12760                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12761                         tooltip : 'This field is required',
12762                         style : this.allowBlank ? ' display:none' : '' 
12763                     },
12764
12765                    inputblock
12766
12767                ];
12768
12769             }
12770
12771         } else {
12772             
12773             cfg.cn = [
12774
12775                     inputblock
12776
12777             ];
12778                 
12779                 
12780         };
12781         
12782         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12783            cfg.cls += ' navbar-form';
12784         }
12785         
12786         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12787             // on BS4 we do this only if not form 
12788             cfg.cls += ' navbar-form';
12789             cfg.tag = 'li';
12790         }
12791         
12792         return cfg;
12793         
12794     },
12795     /**
12796      * return the real input element.
12797      */
12798     inputEl: function ()
12799     {
12800         return this.el.select('input.form-control',true).first();
12801     },
12802     
12803     tooltipEl : function()
12804     {
12805         return this.inputEl();
12806     },
12807     
12808     indicatorEl : function()
12809     {
12810         if (Roo.bootstrap.version == 4) {
12811             return false; // not enabled in v4 yet.
12812         }
12813         
12814         var indicator = this.el.select('i.roo-required-indicator',true).first();
12815         
12816         if(!indicator){
12817             return false;
12818         }
12819         
12820         return indicator;
12821         
12822     },
12823     
12824     setDisabled : function(v)
12825     {
12826         var i  = this.inputEl().dom;
12827         if (!v) {
12828             i.removeAttribute('disabled');
12829             return;
12830             
12831         }
12832         i.setAttribute('disabled','true');
12833     },
12834     initEvents : function()
12835     {
12836           
12837         this.inputEl().on("keydown" , this.fireKey,  this);
12838         this.inputEl().on("focus", this.onFocus,  this);
12839         this.inputEl().on("blur", this.onBlur,  this);
12840         
12841         this.inputEl().relayEvent('keyup', this);
12842         this.inputEl().relayEvent('paste', this);
12843         
12844         this.indicator = this.indicatorEl();
12845         
12846         if(this.indicator){
12847             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12848         }
12849  
12850         // reference to original value for reset
12851         this.originalValue = this.getValue();
12852         //Roo.form.TextField.superclass.initEvents.call(this);
12853         if(this.validationEvent == 'keyup'){
12854             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12855             this.inputEl().on('keyup', this.filterValidation, this);
12856         }
12857         else if(this.validationEvent !== false){
12858             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12859         }
12860         
12861         if(this.selectOnFocus){
12862             this.on("focus", this.preFocus, this);
12863             
12864         }
12865         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12866             this.inputEl().on("keypress", this.filterKeys, this);
12867         } else {
12868             this.inputEl().relayEvent('keypress', this);
12869         }
12870        /* if(this.grow){
12871             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12872             this.el.on("click", this.autoSize,  this);
12873         }
12874         */
12875         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12876             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12877         }
12878         
12879         if (typeof(this.before) == 'object') {
12880             this.before.render(this.el.select('.roo-input-before',true).first());
12881         }
12882         if (typeof(this.after) == 'object') {
12883             this.after.render(this.el.select('.roo-input-after',true).first());
12884         }
12885         
12886         this.inputEl().on('change', this.onChange, this);
12887         
12888     },
12889     filterValidation : function(e){
12890         if(!e.isNavKeyPress()){
12891             this.validationTask.delay(this.validationDelay);
12892         }
12893     },
12894      /**
12895      * Validates the field value
12896      * @return {Boolean} True if the value is valid, else false
12897      */
12898     validate : function(){
12899         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12900         if(this.disabled || this.validateValue(this.getRawValue())){
12901             this.markValid();
12902             return true;
12903         }
12904         
12905         this.markInvalid();
12906         return false;
12907     },
12908     
12909     
12910     /**
12911      * Validates a value according to the field's validation rules and marks the field as invalid
12912      * if the validation fails
12913      * @param {Mixed} value The value to validate
12914      * @return {Boolean} True if the value is valid, else false
12915      */
12916     validateValue : function(value)
12917     {
12918         if(this.getVisibilityEl().hasClass('hidden')){
12919             return true;
12920         }
12921         
12922         if(value.length < 1)  { // if it's blank
12923             if(this.allowBlank){
12924                 return true;
12925             }
12926             return false;
12927         }
12928         
12929         if(value.length < this.minLength){
12930             return false;
12931         }
12932         if(value.length > this.maxLength){
12933             return false;
12934         }
12935         if(this.vtype){
12936             var vt = Roo.form.VTypes;
12937             if(!vt[this.vtype](value, this)){
12938                 return false;
12939             }
12940         }
12941         if(typeof this.validator == "function"){
12942             var msg = this.validator(value);
12943             if (typeof(msg) == 'string') {
12944                 this.invalidText = msg;
12945             }
12946             if(msg !== true){
12947                 return false;
12948             }
12949         }
12950         
12951         if(this.regex && !this.regex.test(value)){
12952             return false;
12953         }
12954         
12955         return true;
12956     },
12957     
12958      // private
12959     fireKey : function(e){
12960         //Roo.log('field ' + e.getKey());
12961         if(e.isNavKeyPress()){
12962             this.fireEvent("specialkey", this, e);
12963         }
12964     },
12965     focus : function (selectText){
12966         if(this.rendered){
12967             this.inputEl().focus();
12968             if(selectText === true){
12969                 this.inputEl().dom.select();
12970             }
12971         }
12972         return this;
12973     } ,
12974     
12975     onFocus : function(){
12976         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12977            // this.el.addClass(this.focusClass);
12978         }
12979         if(!this.hasFocus){
12980             this.hasFocus = true;
12981             this.startValue = this.getValue();
12982             this.fireEvent("focus", this);
12983         }
12984     },
12985     
12986     beforeBlur : Roo.emptyFn,
12987
12988     
12989     // private
12990     onBlur : function(){
12991         this.beforeBlur();
12992         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12993             //this.el.removeClass(this.focusClass);
12994         }
12995         this.hasFocus = false;
12996         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12997             this.validate();
12998         }
12999         var v = this.getValue();
13000         if(String(v) !== String(this.startValue)){
13001             this.fireEvent('change', this, v, this.startValue);
13002         }
13003         this.fireEvent("blur", this);
13004     },
13005     
13006     onChange : function(e)
13007     {
13008         var v = this.getValue();
13009         if(String(v) !== String(this.startValue)){
13010             this.fireEvent('change', this, v, this.startValue);
13011         }
13012         
13013     },
13014     
13015     /**
13016      * Resets the current field value to the originally loaded value and clears any validation messages
13017      */
13018     reset : function(){
13019         this.setValue(this.originalValue);
13020         this.validate();
13021     },
13022      /**
13023      * Returns the name of the field
13024      * @return {Mixed} name The name field
13025      */
13026     getName: function(){
13027         return this.name;
13028     },
13029      /**
13030      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13031      * @return {Mixed} value The field value
13032      */
13033     getValue : function(){
13034         
13035         var v = this.inputEl().getValue();
13036         
13037         return v;
13038     },
13039     /**
13040      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13041      * @return {Mixed} value The field value
13042      */
13043     getRawValue : function(){
13044         var v = this.inputEl().getValue();
13045         
13046         return v;
13047     },
13048     
13049     /**
13050      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13051      * @param {Mixed} value The value to set
13052      */
13053     setRawValue : function(v){
13054         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13055     },
13056     
13057     selectText : function(start, end){
13058         var v = this.getRawValue();
13059         if(v.length > 0){
13060             start = start === undefined ? 0 : start;
13061             end = end === undefined ? v.length : end;
13062             var d = this.inputEl().dom;
13063             if(d.setSelectionRange){
13064                 d.setSelectionRange(start, end);
13065             }else if(d.createTextRange){
13066                 var range = d.createTextRange();
13067                 range.moveStart("character", start);
13068                 range.moveEnd("character", v.length-end);
13069                 range.select();
13070             }
13071         }
13072     },
13073     
13074     /**
13075      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13076      * @param {Mixed} value The value to set
13077      */
13078     setValue : function(v){
13079         this.value = v;
13080         if(this.rendered){
13081             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13082             this.validate();
13083         }
13084     },
13085     
13086     /*
13087     processValue : function(value){
13088         if(this.stripCharsRe){
13089             var newValue = value.replace(this.stripCharsRe, '');
13090             if(newValue !== value){
13091                 this.setRawValue(newValue);
13092                 return newValue;
13093             }
13094         }
13095         return value;
13096     },
13097   */
13098     preFocus : function(){
13099         
13100         if(this.selectOnFocus){
13101             this.inputEl().dom.select();
13102         }
13103     },
13104     filterKeys : function(e){
13105         var k = e.getKey();
13106         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13107             return;
13108         }
13109         var c = e.getCharCode(), cc = String.fromCharCode(c);
13110         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13111             return;
13112         }
13113         if(!this.maskRe.test(cc)){
13114             e.stopEvent();
13115         }
13116     },
13117      /**
13118      * Clear any invalid styles/messages for this field
13119      */
13120     clearInvalid : function(){
13121         
13122         if(!this.el || this.preventMark){ // not rendered
13123             return;
13124         }
13125         
13126         
13127         this.el.removeClass([this.invalidClass, 'is-invalid']);
13128         
13129         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13130             
13131             var feedback = this.el.select('.form-control-feedback', true).first();
13132             
13133             if(feedback){
13134                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13135             }
13136             
13137         }
13138         
13139         if(this.indicator){
13140             this.indicator.removeClass('visible');
13141             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13142         }
13143         
13144         this.fireEvent('valid', this);
13145     },
13146     
13147      /**
13148      * Mark this field as valid
13149      */
13150     markValid : function()
13151     {
13152         if(!this.el  || this.preventMark){ // not rendered...
13153             return;
13154         }
13155         
13156         this.el.removeClass([this.invalidClass, this.validClass]);
13157         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13158
13159         var feedback = this.el.select('.form-control-feedback', true).first();
13160             
13161         if(feedback){
13162             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13163         }
13164         
13165         if(this.indicator){
13166             this.indicator.removeClass('visible');
13167             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13168         }
13169         
13170         if(this.disabled){
13171             return;
13172         }
13173         
13174            
13175         if(this.allowBlank && !this.getRawValue().length){
13176             return;
13177         }
13178         if (Roo.bootstrap.version == 3) {
13179             this.el.addClass(this.validClass);
13180         } else {
13181             this.inputEl().addClass('is-valid');
13182         }
13183
13184         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13185             
13186             var feedback = this.el.select('.form-control-feedback', true).first();
13187             
13188             if(feedback){
13189                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13190                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13191             }
13192             
13193         }
13194         
13195         this.fireEvent('valid', this);
13196     },
13197     
13198      /**
13199      * Mark this field as invalid
13200      * @param {String} msg The validation message
13201      */
13202     markInvalid : function(msg)
13203     {
13204         if(!this.el  || this.preventMark){ // not rendered
13205             return;
13206         }
13207         
13208         this.el.removeClass([this.invalidClass, this.validClass]);
13209         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13210         
13211         var feedback = this.el.select('.form-control-feedback', true).first();
13212             
13213         if(feedback){
13214             this.el.select('.form-control-feedback', true).first().removeClass(
13215                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13216         }
13217
13218         if(this.disabled){
13219             return;
13220         }
13221         
13222         if(this.allowBlank && !this.getRawValue().length){
13223             return;
13224         }
13225         
13226         if(this.indicator){
13227             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13228             this.indicator.addClass('visible');
13229         }
13230         if (Roo.bootstrap.version == 3) {
13231             this.el.addClass(this.invalidClass);
13232         } else {
13233             this.inputEl().addClass('is-invalid');
13234         }
13235         
13236         
13237         
13238         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13239             
13240             var feedback = this.el.select('.form-control-feedback', true).first();
13241             
13242             if(feedback){
13243                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13244                 
13245                 if(this.getValue().length || this.forceFeedback){
13246                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13247                 }
13248                 
13249             }
13250             
13251         }
13252         
13253         this.fireEvent('invalid', this, msg);
13254     },
13255     // private
13256     SafariOnKeyDown : function(event)
13257     {
13258         // this is a workaround for a password hang bug on chrome/ webkit.
13259         if (this.inputEl().dom.type != 'password') {
13260             return;
13261         }
13262         
13263         var isSelectAll = false;
13264         
13265         if(this.inputEl().dom.selectionEnd > 0){
13266             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13267         }
13268         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13269             event.preventDefault();
13270             this.setValue('');
13271             return;
13272         }
13273         
13274         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13275             
13276             event.preventDefault();
13277             // this is very hacky as keydown always get's upper case.
13278             //
13279             var cc = String.fromCharCode(event.getCharCode());
13280             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13281             
13282         }
13283     },
13284     adjustWidth : function(tag, w){
13285         tag = tag.toLowerCase();
13286         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13287             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13288                 if(tag == 'input'){
13289                     return w + 2;
13290                 }
13291                 if(tag == 'textarea'){
13292                     return w-2;
13293                 }
13294             }else if(Roo.isOpera){
13295                 if(tag == 'input'){
13296                     return w + 2;
13297                 }
13298                 if(tag == 'textarea'){
13299                     return w-2;
13300                 }
13301             }
13302         }
13303         return w;
13304     },
13305     
13306     setFieldLabel : function(v)
13307     {
13308         if(!this.rendered){
13309             return;
13310         }
13311         
13312         if(this.indicatorEl()){
13313             var ar = this.el.select('label > span',true);
13314             
13315             if (ar.elements.length) {
13316                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13317                 this.fieldLabel = v;
13318                 return;
13319             }
13320             
13321             var br = this.el.select('label',true);
13322             
13323             if(br.elements.length) {
13324                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13325                 this.fieldLabel = v;
13326                 return;
13327             }
13328             
13329             Roo.log('Cannot Found any of label > span || label in input');
13330             return;
13331         }
13332         
13333         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13334         this.fieldLabel = v;
13335         
13336         
13337     }
13338 });
13339
13340  
13341 /*
13342  * - LGPL
13343  *
13344  * Input
13345  * 
13346  */
13347
13348 /**
13349  * @class Roo.bootstrap.form.TextArea
13350  * @extends Roo.bootstrap.form.Input
13351  * Bootstrap TextArea class
13352  * @cfg {Number} cols Specifies the visible width of a text area
13353  * @cfg {Number} rows Specifies the visible number of lines in a text area
13354  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13355  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13356  * @cfg {string} html text
13357  * 
13358  * @constructor
13359  * Create a new TextArea
13360  * @param {Object} config The config object
13361  */
13362
13363 Roo.bootstrap.form.TextArea = function(config){
13364     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13365    
13366 };
13367
13368 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13369      
13370     cols : false,
13371     rows : 5,
13372     readOnly : false,
13373     warp : 'soft',
13374     resize : false,
13375     value: false,
13376     html: false,
13377     
13378     getAutoCreate : function(){
13379         
13380         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13381         
13382         var id = Roo.id();
13383         
13384         var cfg = {};
13385         
13386         if(this.inputType != 'hidden'){
13387             cfg.cls = 'form-group' //input-group
13388         }
13389         
13390         var input =  {
13391             tag: 'textarea',
13392             id : id,
13393             warp : this.warp,
13394             rows : this.rows,
13395             value : this.value || '',
13396             html: this.html || '',
13397             cls : 'form-control',
13398             placeholder : this.placeholder || '' 
13399             
13400         };
13401         
13402         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13403             input.maxLength = this.maxLength;
13404         }
13405         
13406         if(this.resize){
13407             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13408         }
13409         
13410         if(this.cols){
13411             input.cols = this.cols;
13412         }
13413         
13414         if (this.readOnly) {
13415             input.readonly = true;
13416         }
13417         
13418         if (this.name) {
13419             input.name = this.name;
13420         }
13421         
13422         if (this.size) {
13423             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13424         }
13425         
13426         var settings=this;
13427         ['xs','sm','md','lg'].map(function(size){
13428             if (settings[size]) {
13429                 cfg.cls += ' col-' + size + '-' + settings[size];
13430             }
13431         });
13432         
13433         var inputblock = input;
13434         
13435         if(this.hasFeedback && !this.allowBlank){
13436             
13437             var feedback = {
13438                 tag: 'span',
13439                 cls: 'glyphicon form-control-feedback'
13440             };
13441
13442             inputblock = {
13443                 cls : 'has-feedback',
13444                 cn :  [
13445                     input,
13446                     feedback
13447                 ] 
13448             };  
13449         }
13450         
13451         
13452         if (this.before || this.after) {
13453             
13454             inputblock = {
13455                 cls : 'input-group',
13456                 cn :  [] 
13457             };
13458             if (this.before) {
13459                 inputblock.cn.push({
13460                     tag :'span',
13461                     cls : 'input-group-addon',
13462                     html : this.before
13463                 });
13464             }
13465             
13466             inputblock.cn.push(input);
13467             
13468             if(this.hasFeedback && !this.allowBlank){
13469                 inputblock.cls += ' has-feedback';
13470                 inputblock.cn.push(feedback);
13471             }
13472             
13473             if (this.after) {
13474                 inputblock.cn.push({
13475                     tag :'span',
13476                     cls : 'input-group-addon',
13477                     html : this.after
13478                 });
13479             }
13480             
13481         }
13482         
13483         if (align ==='left' && this.fieldLabel.length) {
13484             cfg.cn = [
13485                 {
13486                     tag: 'label',
13487                     'for' :  id,
13488                     cls : 'control-label',
13489                     html : this.fieldLabel
13490                 },
13491                 {
13492                     cls : "",
13493                     cn: [
13494                         inputblock
13495                     ]
13496                 }
13497
13498             ];
13499             
13500             if(this.labelWidth > 12){
13501                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13502             }
13503
13504             if(this.labelWidth < 13 && this.labelmd == 0){
13505                 this.labelmd = this.labelWidth;
13506             }
13507
13508             if(this.labellg > 0){
13509                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13510                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13511             }
13512
13513             if(this.labelmd > 0){
13514                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13515                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13516             }
13517
13518             if(this.labelsm > 0){
13519                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13520                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13521             }
13522
13523             if(this.labelxs > 0){
13524                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13525                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13526             }
13527             
13528         } else if ( this.fieldLabel.length) {
13529             cfg.cn = [
13530
13531                {
13532                    tag: 'label',
13533                    //cls : 'input-group-addon',
13534                    html : this.fieldLabel
13535
13536                },
13537
13538                inputblock
13539
13540            ];
13541
13542         } else {
13543
13544             cfg.cn = [
13545
13546                 inputblock
13547
13548             ];
13549                 
13550         }
13551         
13552         if (this.disabled) {
13553             input.disabled=true;
13554         }
13555         
13556         return cfg;
13557         
13558     },
13559     /**
13560      * return the real textarea element.
13561      */
13562     inputEl: function ()
13563     {
13564         return this.el.select('textarea.form-control',true).first();
13565     },
13566     
13567     /**
13568      * Clear any invalid styles/messages for this field
13569      */
13570     clearInvalid : function()
13571     {
13572         
13573         if(!this.el || this.preventMark){ // not rendered
13574             return;
13575         }
13576         
13577         var label = this.el.select('label', true).first();
13578         var icon = this.el.select('i.fa-star', true).first();
13579         
13580         if(label && icon){
13581             icon.remove();
13582         }
13583         this.el.removeClass( this.validClass);
13584         this.inputEl().removeClass('is-invalid');
13585          
13586         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13587             
13588             var feedback = this.el.select('.form-control-feedback', true).first();
13589             
13590             if(feedback){
13591                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13592             }
13593             
13594         }
13595         
13596         this.fireEvent('valid', this);
13597     },
13598     
13599      /**
13600      * Mark this field as valid
13601      */
13602     markValid : function()
13603     {
13604         if(!this.el  || this.preventMark){ // not rendered
13605             return;
13606         }
13607         
13608         this.el.removeClass([this.invalidClass, this.validClass]);
13609         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13610         
13611         var feedback = this.el.select('.form-control-feedback', true).first();
13612             
13613         if(feedback){
13614             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13615         }
13616
13617         if(this.disabled || this.allowBlank){
13618             return;
13619         }
13620         
13621         var label = this.el.select('label', true).first();
13622         var icon = this.el.select('i.fa-star', true).first();
13623         
13624         if(label && icon){
13625             icon.remove();
13626         }
13627         if (Roo.bootstrap.version == 3) {
13628             this.el.addClass(this.validClass);
13629         } else {
13630             this.inputEl().addClass('is-valid');
13631         }
13632         
13633         
13634         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13635             
13636             var feedback = this.el.select('.form-control-feedback', true).first();
13637             
13638             if(feedback){
13639                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13640                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13641             }
13642             
13643         }
13644         
13645         this.fireEvent('valid', this);
13646     },
13647     
13648      /**
13649      * Mark this field as invalid
13650      * @param {String} msg The validation message
13651      */
13652     markInvalid : function(msg)
13653     {
13654         if(!this.el  || this.preventMark){ // not rendered
13655             return;
13656         }
13657         
13658         this.el.removeClass([this.invalidClass, this.validClass]);
13659         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13660         
13661         var feedback = this.el.select('.form-control-feedback', true).first();
13662             
13663         if(feedback){
13664             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13665         }
13666
13667         if(this.disabled || this.allowBlank){
13668             return;
13669         }
13670         
13671         var label = this.el.select('label', true).first();
13672         var icon = this.el.select('i.fa-star', true).first();
13673         
13674         if(!this.getValue().length && label && !icon){
13675             this.el.createChild({
13676                 tag : 'i',
13677                 cls : 'text-danger fa fa-lg fa-star',
13678                 tooltip : 'This field is required',
13679                 style : 'margin-right:5px;'
13680             }, label, true);
13681         }
13682         
13683         if (Roo.bootstrap.version == 3) {
13684             this.el.addClass(this.invalidClass);
13685         } else {
13686             this.inputEl().addClass('is-invalid');
13687         }
13688         
13689         // fixme ... this may be depricated need to test..
13690         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13691             
13692             var feedback = this.el.select('.form-control-feedback', true).first();
13693             
13694             if(feedback){
13695                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13696                 
13697                 if(this.getValue().length || this.forceFeedback){
13698                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13699                 }
13700                 
13701             }
13702             
13703         }
13704         
13705         this.fireEvent('invalid', this, msg);
13706     }
13707 });
13708
13709  
13710 /*
13711  * - LGPL
13712  *
13713  * trigger field - base class for combo..
13714  * 
13715  */
13716  
13717 /**
13718  * @class Roo.bootstrap.form.TriggerField
13719  * @extends Roo.bootstrap.form.Input
13720  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13721  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13722  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13723  * for which you can provide a custom implementation.  For example:
13724  * <pre><code>
13725 var trigger = new Roo.bootstrap.form.TriggerField();
13726 trigger.onTriggerClick = myTriggerFn;
13727 trigger.applyTo('my-field');
13728 </code></pre>
13729  *
13730  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13731  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13732  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13733  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13734  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13735
13736  * @constructor
13737  * Create a new TriggerField.
13738  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13739  * to the base TextField)
13740  */
13741 Roo.bootstrap.form.TriggerField = function(config){
13742     this.mimicing = false;
13743     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13744 };
13745
13746 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13747     /**
13748      * @cfg {String} triggerClass A CSS class to apply to the trigger
13749      */
13750      /**
13751      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13752      */
13753     hideTrigger:false,
13754
13755     /**
13756      * @cfg {Boolean} removable (true|false) special filter default false
13757      */
13758     removable : false,
13759     
13760     /** @cfg {Boolean} grow @hide */
13761     /** @cfg {Number} growMin @hide */
13762     /** @cfg {Number} growMax @hide */
13763
13764     /**
13765      * @hide 
13766      * @method
13767      */
13768     autoSize: Roo.emptyFn,
13769     // private
13770     monitorTab : true,
13771     // private
13772     deferHeight : true,
13773
13774     
13775     actionMode : 'wrap',
13776     
13777     caret : false,
13778     
13779     
13780     getAutoCreate : function(){
13781        
13782         var align = this.labelAlign || this.parentLabelAlign();
13783         
13784         var id = Roo.id();
13785         
13786         var cfg = {
13787             cls: 'form-group' //input-group
13788         };
13789         
13790         
13791         var input =  {
13792             tag: 'input',
13793             id : id,
13794             type : this.inputType,
13795             cls : 'form-control',
13796             autocomplete: 'new-password',
13797             placeholder : this.placeholder || '' 
13798             
13799         };
13800         if (this.name) {
13801             input.name = this.name;
13802         }
13803         if (this.size) {
13804             input.cls += ' input-' + this.size;
13805         }
13806         
13807         if (this.disabled) {
13808             input.disabled=true;
13809         }
13810         
13811         var inputblock = input;
13812         
13813         if(this.hasFeedback && !this.allowBlank){
13814             
13815             var feedback = {
13816                 tag: 'span',
13817                 cls: 'glyphicon form-control-feedback'
13818             };
13819             
13820             if(this.removable && !this.editable  ){
13821                 inputblock = {
13822                     cls : 'has-feedback',
13823                     cn :  [
13824                         inputblock,
13825                         {
13826                             tag: 'button',
13827                             html : 'x',
13828                             cls : 'roo-combo-removable-btn close'
13829                         },
13830                         feedback
13831                     ] 
13832                 };
13833             } else {
13834                 inputblock = {
13835                     cls : 'has-feedback',
13836                     cn :  [
13837                         inputblock,
13838                         feedback
13839                     ] 
13840                 };
13841             }
13842
13843         } else {
13844             if(this.removable && !this.editable ){
13845                 inputblock = {
13846                     cls : 'roo-removable',
13847                     cn :  [
13848                         inputblock,
13849                         {
13850                             tag: 'button',
13851                             html : 'x',
13852                             cls : 'roo-combo-removable-btn close'
13853                         }
13854                     ] 
13855                 };
13856             }
13857         }
13858         
13859         if (this.before || this.after) {
13860             
13861             inputblock = {
13862                 cls : 'input-group',
13863                 cn :  [] 
13864             };
13865             if (this.before) {
13866                 inputblock.cn.push({
13867                     tag :'span',
13868                     cls : 'input-group-addon input-group-prepend input-group-text',
13869                     html : this.before
13870                 });
13871             }
13872             
13873             inputblock.cn.push(input);
13874             
13875             if(this.hasFeedback && !this.allowBlank){
13876                 inputblock.cls += ' has-feedback';
13877                 inputblock.cn.push(feedback);
13878             }
13879             
13880             if (this.after) {
13881                 inputblock.cn.push({
13882                     tag :'span',
13883                     cls : 'input-group-addon input-group-append input-group-text',
13884                     html : this.after
13885                 });
13886             }
13887             
13888         };
13889         
13890       
13891         
13892         var ibwrap = inputblock;
13893         
13894         if(this.multiple){
13895             ibwrap = {
13896                 tag: 'ul',
13897                 cls: 'roo-select2-choices',
13898                 cn:[
13899                     {
13900                         tag: 'li',
13901                         cls: 'roo-select2-search-field',
13902                         cn: [
13903
13904                             inputblock
13905                         ]
13906                     }
13907                 ]
13908             };
13909                 
13910         }
13911         
13912         var combobox = {
13913             cls: 'roo-select2-container input-group',
13914             cn: [
13915                  {
13916                     tag: 'input',
13917                     type : 'hidden',
13918                     cls: 'form-hidden-field'
13919                 },
13920                 ibwrap
13921             ]
13922         };
13923         
13924         if(!this.multiple && this.showToggleBtn){
13925             
13926             var caret = {
13927                         tag: 'span',
13928                         cls: 'caret'
13929              };
13930             if (this.caret != false) {
13931                 caret = {
13932                      tag: 'i',
13933                      cls: 'fa fa-' + this.caret
13934                 };
13935                 
13936             }
13937             
13938             combobox.cn.push({
13939                 tag :'span',
13940                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13941                 cn : [
13942                     Roo.bootstrap.version == 3 ? caret : '',
13943                     {
13944                         tag: 'span',
13945                         cls: 'combobox-clear',
13946                         cn  : [
13947                             {
13948                                 tag : 'i',
13949                                 cls: 'icon-remove'
13950                             }
13951                         ]
13952                     }
13953                 ]
13954
13955             })
13956         }
13957         
13958         if(this.multiple){
13959             combobox.cls += ' roo-select2-container-multi';
13960         }
13961          var indicator = {
13962             tag : 'i',
13963             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13964             tooltip : 'This field is required'
13965         };
13966         if (Roo.bootstrap.version == 4) {
13967             indicator = {
13968                 tag : 'i',
13969                 style : 'display:none'
13970             };
13971         }
13972         
13973         
13974         if (align ==='left' && this.fieldLabel.length) {
13975             
13976             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13977
13978             cfg.cn = [
13979                 indicator,
13980                 {
13981                     tag: 'label',
13982                     'for' :  id,
13983                     cls : 'control-label',
13984                     html : this.fieldLabel
13985
13986                 },
13987                 {
13988                     cls : "", 
13989                     cn: [
13990                         combobox
13991                     ]
13992                 }
13993
13994             ];
13995             
13996             var labelCfg = cfg.cn[1];
13997             var contentCfg = cfg.cn[2];
13998             
13999             if(this.indicatorpos == 'right'){
14000                 cfg.cn = [
14001                     {
14002                         tag: 'label',
14003                         'for' :  id,
14004                         cls : 'control-label',
14005                         cn : [
14006                             {
14007                                 tag : 'span',
14008                                 html : this.fieldLabel
14009                             },
14010                             indicator
14011                         ]
14012                     },
14013                     {
14014                         cls : "", 
14015                         cn: [
14016                             combobox
14017                         ]
14018                     }
14019
14020                 ];
14021                 
14022                 labelCfg = cfg.cn[0];
14023                 contentCfg = cfg.cn[1];
14024             }
14025             
14026             if(this.labelWidth > 12){
14027                 labelCfg.style = "width: " + this.labelWidth + 'px';
14028             }
14029             
14030             if(this.labelWidth < 13 && this.labelmd == 0){
14031                 this.labelmd = this.labelWidth;
14032             }
14033             
14034             if(this.labellg > 0){
14035                 labelCfg.cls += ' col-lg-' + this.labellg;
14036                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14037             }
14038             
14039             if(this.labelmd > 0){
14040                 labelCfg.cls += ' col-md-' + this.labelmd;
14041                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14042             }
14043             
14044             if(this.labelsm > 0){
14045                 labelCfg.cls += ' col-sm-' + this.labelsm;
14046                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14047             }
14048             
14049             if(this.labelxs > 0){
14050                 labelCfg.cls += ' col-xs-' + this.labelxs;
14051                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14052             }
14053             
14054         } else if ( this.fieldLabel.length) {
14055 //                Roo.log(" label");
14056             cfg.cn = [
14057                 indicator,
14058                {
14059                    tag: 'label',
14060                    //cls : 'input-group-addon',
14061                    html : this.fieldLabel
14062
14063                },
14064
14065                combobox
14066
14067             ];
14068             
14069             if(this.indicatorpos == 'right'){
14070                 
14071                 cfg.cn = [
14072                     {
14073                        tag: 'label',
14074                        cn : [
14075                            {
14076                                tag : 'span',
14077                                html : this.fieldLabel
14078                            },
14079                            indicator
14080                        ]
14081
14082                     },
14083                     combobox
14084
14085                 ];
14086
14087             }
14088
14089         } else {
14090             
14091 //                Roo.log(" no label && no align");
14092                 cfg = combobox
14093                      
14094                 
14095         }
14096         
14097         var settings=this;
14098         ['xs','sm','md','lg'].map(function(size){
14099             if (settings[size]) {
14100                 cfg.cls += ' col-' + size + '-' + settings[size];
14101             }
14102         });
14103         
14104         return cfg;
14105         
14106     },
14107     
14108     
14109     
14110     // private
14111     onResize : function(w, h){
14112 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14113 //        if(typeof w == 'number'){
14114 //            var x = w - this.trigger.getWidth();
14115 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14116 //            this.trigger.setStyle('left', x+'px');
14117 //        }
14118     },
14119
14120     // private
14121     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14122
14123     // private
14124     getResizeEl : function(){
14125         return this.inputEl();
14126     },
14127
14128     // private
14129     getPositionEl : function(){
14130         return this.inputEl();
14131     },
14132
14133     // private
14134     alignErrorIcon : function(){
14135         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14136     },
14137
14138     // private
14139     initEvents : function(){
14140         
14141         this.createList();
14142         
14143         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14144         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14145         if(!this.multiple && this.showToggleBtn){
14146             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14147             if(this.hideTrigger){
14148                 this.trigger.setDisplayed(false);
14149             }
14150             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14151         }
14152         
14153         if(this.multiple){
14154             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14155         }
14156         
14157         if(this.removable && !this.editable && !this.tickable){
14158             var close = this.closeTriggerEl();
14159             
14160             if(close){
14161                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14162                 close.on('click', this.removeBtnClick, this, close);
14163             }
14164         }
14165         
14166         //this.trigger.addClassOnOver('x-form-trigger-over');
14167         //this.trigger.addClassOnClick('x-form-trigger-click');
14168         
14169         //if(!this.width){
14170         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14171         //}
14172     },
14173     
14174     closeTriggerEl : function()
14175     {
14176         var close = this.el.select('.roo-combo-removable-btn', true).first();
14177         return close ? close : false;
14178     },
14179     
14180     removeBtnClick : function(e, h, el)
14181     {
14182         e.preventDefault();
14183         
14184         if(this.fireEvent("remove", this) !== false){
14185             this.reset();
14186             this.fireEvent("afterremove", this)
14187         }
14188     },
14189     
14190     createList : function()
14191     {
14192         this.list = Roo.get(document.body).createChild({
14193             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14194             cls: 'typeahead typeahead-long dropdown-menu shadow',
14195             style: 'display:none'
14196         });
14197         
14198         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14199         
14200     },
14201
14202     // private
14203     initTrigger : function(){
14204        
14205     },
14206
14207     // private
14208     onDestroy : function(){
14209         if(this.trigger){
14210             this.trigger.removeAllListeners();
14211           //  this.trigger.remove();
14212         }
14213         //if(this.wrap){
14214         //    this.wrap.remove();
14215         //}
14216         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14217     },
14218
14219     // private
14220     onFocus : function(){
14221         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14222         /*
14223         if(!this.mimicing){
14224             this.wrap.addClass('x-trigger-wrap-focus');
14225             this.mimicing = true;
14226             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14227             if(this.monitorTab){
14228                 this.el.on("keydown", this.checkTab, this);
14229             }
14230         }
14231         */
14232     },
14233
14234     // private
14235     checkTab : function(e){
14236         if(e.getKey() == e.TAB){
14237             this.triggerBlur();
14238         }
14239     },
14240
14241     // private
14242     onBlur : function(){
14243         // do nothing
14244     },
14245
14246     // private
14247     mimicBlur : function(e, t){
14248         /*
14249         if(!this.wrap.contains(t) && this.validateBlur()){
14250             this.triggerBlur();
14251         }
14252         */
14253     },
14254
14255     // private
14256     triggerBlur : function(){
14257         this.mimicing = false;
14258         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14259         if(this.monitorTab){
14260             this.el.un("keydown", this.checkTab, this);
14261         }
14262         //this.wrap.removeClass('x-trigger-wrap-focus');
14263         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14264     },
14265
14266     // private
14267     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14268     validateBlur : function(e, t){
14269         return true;
14270     },
14271
14272     // private
14273     onDisable : function(){
14274         this.inputEl().dom.disabled = true;
14275         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14276         //if(this.wrap){
14277         //    this.wrap.addClass('x-item-disabled');
14278         //}
14279     },
14280
14281     // private
14282     onEnable : function(){
14283         this.inputEl().dom.disabled = false;
14284         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14285         //if(this.wrap){
14286         //    this.el.removeClass('x-item-disabled');
14287         //}
14288     },
14289
14290     // private
14291     onShow : function(){
14292         var ae = this.getActionEl();
14293         
14294         if(ae){
14295             ae.dom.style.display = '';
14296             ae.dom.style.visibility = 'visible';
14297         }
14298     },
14299
14300     // private
14301     
14302     onHide : function(){
14303         var ae = this.getActionEl();
14304         ae.dom.style.display = 'none';
14305     },
14306
14307     /**
14308      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14309      * by an implementing function.
14310      * @method
14311      * @param {EventObject} e
14312      */
14313     onTriggerClick : Roo.emptyFn
14314 });
14315  
14316 /*
14317 * Licence: LGPL
14318 */
14319
14320 /**
14321  * @class Roo.bootstrap.form.CardUploader
14322  * @extends Roo.bootstrap.Button
14323  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14324  * @cfg {Number} errorTimeout default 3000
14325  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14326  * @cfg {Array}  html The button text.
14327
14328  *
14329  * @constructor
14330  * Create a new CardUploader
14331  * @param {Object} config The config object
14332  */
14333
14334 Roo.bootstrap.form.CardUploader = function(config){
14335     
14336  
14337     
14338     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14339     
14340     
14341     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14342         return r.data.id
14343      });
14344     
14345      this.addEvents({
14346          // raw events
14347         /**
14348          * @event preview
14349          * When a image is clicked on - and needs to display a slideshow or similar..
14350          * @param {Roo.bootstrap.Card} this
14351          * @param {Object} The image information data 
14352          *
14353          */
14354         'preview' : true,
14355          /**
14356          * @event download
14357          * When a the download link is clicked
14358          * @param {Roo.bootstrap.Card} this
14359          * @param {Object} The image information data  contains 
14360          */
14361         'download' : true
14362         
14363     });
14364 };
14365  
14366 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14367     
14368      
14369     errorTimeout : 3000,
14370      
14371     images : false,
14372    
14373     fileCollection : false,
14374     allowBlank : true,
14375     
14376     getAutoCreate : function()
14377     {
14378         
14379         var cfg =  {
14380             cls :'form-group' ,
14381             cn : [
14382                
14383                 {
14384                     tag: 'label',
14385                    //cls : 'input-group-addon',
14386                     html : this.fieldLabel
14387
14388                 },
14389
14390                 {
14391                     tag: 'input',
14392                     type : 'hidden',
14393                     name : this.name,
14394                     value : this.value,
14395                     cls : 'd-none  form-control'
14396                 },
14397                 
14398                 {
14399                     tag: 'input',
14400                     multiple : 'multiple',
14401                     type : 'file',
14402                     cls : 'd-none  roo-card-upload-selector'
14403                 },
14404                 
14405                 {
14406                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14407                 },
14408                 {
14409                     cls : 'card-columns roo-card-uploader-container'
14410                 }
14411
14412             ]
14413         };
14414            
14415          
14416         return cfg;
14417     },
14418     
14419     getChildContainer : function() /// what children are added to.
14420     {
14421         return this.containerEl;
14422     },
14423    
14424     getButtonContainer : function() /// what children are added to.
14425     {
14426         return this.el.select(".roo-card-uploader-button-container").first();
14427     },
14428    
14429     initEvents : function()
14430     {
14431         
14432         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14433         
14434         var t = this;
14435         this.addxtype({
14436             xns: Roo.bootstrap,
14437
14438             xtype : 'Button',
14439             container_method : 'getButtonContainer' ,            
14440             html :  this.html, // fix changable?
14441             cls : 'w-100 ',
14442             listeners : {
14443                 'click' : function(btn, e) {
14444                     t.onClick(e);
14445                 }
14446             }
14447         });
14448         
14449         
14450         
14451         
14452         this.urlAPI = (window.createObjectURL && window) || 
14453                                 (window.URL && URL.revokeObjectURL && URL) || 
14454                                 (window.webkitURL && webkitURL);
14455                         
14456          
14457          
14458          
14459         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14460         
14461         this.selectorEl.on('change', this.onFileSelected, this);
14462         if (this.images) {
14463             var t = this;
14464             this.images.forEach(function(img) {
14465                 t.addCard(img)
14466             });
14467             this.images = false;
14468         }
14469         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14470          
14471        
14472     },
14473     
14474    
14475     onClick : function(e)
14476     {
14477         e.preventDefault();
14478          
14479         this.selectorEl.dom.click();
14480          
14481     },
14482     
14483     onFileSelected : function(e)
14484     {
14485         e.preventDefault();
14486         
14487         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14488             return;
14489         }
14490         
14491         Roo.each(this.selectorEl.dom.files, function(file){    
14492             this.addFile(file);
14493         }, this);
14494          
14495     },
14496     
14497       
14498     
14499       
14500     
14501     addFile : function(file)
14502     {
14503            
14504         if(typeof(file) === 'string'){
14505             throw "Add file by name?"; // should not happen
14506             return;
14507         }
14508         
14509         if(!file || !this.urlAPI){
14510             return;
14511         }
14512         
14513         // file;
14514         // file.type;
14515         
14516         var _this = this;
14517         
14518         
14519         var url = _this.urlAPI.createObjectURL( file);
14520            
14521         this.addCard({
14522             id : Roo.bootstrap.form.CardUploader.ID--,
14523             is_uploaded : false,
14524             src : url,
14525             srcfile : file,
14526             title : file.name,
14527             mimetype : file.type,
14528             preview : false,
14529             is_deleted : 0
14530         });
14531         
14532     },
14533     
14534     /**
14535      * addCard - add an Attachment to the uploader
14536      * @param data - the data about the image to upload
14537      *
14538      * {
14539           id : 123
14540           title : "Title of file",
14541           is_uploaded : false,
14542           src : "http://.....",
14543           srcfile : { the File upload object },
14544           mimetype : file.type,
14545           preview : false,
14546           is_deleted : 0
14547           .. any other data...
14548         }
14549      *
14550      * 
14551     */
14552     
14553     addCard : function (data)
14554     {
14555         // hidden input element?
14556         // if the file is not an image...
14557         //then we need to use something other that and header_image
14558         var t = this;
14559         //   remove.....
14560         var footer = [
14561             {
14562                 xns : Roo.bootstrap,
14563                 xtype : 'CardFooter',
14564                  items: [
14565                     {
14566                         xns : Roo.bootstrap,
14567                         xtype : 'Element',
14568                         cls : 'd-flex',
14569                         items : [
14570                             
14571                             {
14572                                 xns : Roo.bootstrap,
14573                                 xtype : 'Button',
14574                                 html : String.format("<small>{0}</small>", data.title),
14575                                 cls : 'col-10 text-left',
14576                                 size: 'sm',
14577                                 weight: 'link',
14578                                 fa : 'download',
14579                                 listeners : {
14580                                     click : function() {
14581                                      
14582                                         t.fireEvent( "download", t, data );
14583                                     }
14584                                 }
14585                             },
14586                           
14587                             {
14588                                 xns : Roo.bootstrap,
14589                                 xtype : 'Button',
14590                                 style: 'max-height: 28px; ',
14591                                 size : 'sm',
14592                                 weight: 'danger',
14593                                 cls : 'col-2',
14594                                 fa : 'times',
14595                                 listeners : {
14596                                     click : function() {
14597                                         t.removeCard(data.id)
14598                                     }
14599                                 }
14600                             }
14601                         ]
14602                     }
14603                     
14604                 ] 
14605             }
14606             
14607         ];
14608         
14609         var cn = this.addxtype(
14610             {
14611                  
14612                 xns : Roo.bootstrap,
14613                 xtype : 'Card',
14614                 closeable : true,
14615                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14616                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14617                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14618                 data : data,
14619                 html : false,
14620                  
14621                 items : footer,
14622                 initEvents : function() {
14623                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14624                     var card = this;
14625                     this.imgEl = this.el.select('.card-img-top').first();
14626                     if (this.imgEl) {
14627                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14628                         this.imgEl.set({ 'pointer' : 'cursor' });
14629                                   
14630                     }
14631                     this.getCardFooter().addClass('p-1');
14632                     
14633                   
14634                 }
14635                 
14636             }
14637         );
14638         // dont' really need ot update items.
14639         // this.items.push(cn);
14640         this.fileCollection.add(cn);
14641         
14642         if (!data.srcfile) {
14643             this.updateInput();
14644             return;
14645         }
14646             
14647         var _t = this;
14648         var reader = new FileReader();
14649         reader.addEventListener("load", function() {  
14650             data.srcdata =  reader.result;
14651             _t.updateInput();
14652         });
14653         reader.readAsDataURL(data.srcfile);
14654         
14655         
14656         
14657     },
14658     removeCard : function(id)
14659     {
14660         
14661         var card  = this.fileCollection.get(id);
14662         card.data.is_deleted = 1;
14663         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14664         //this.fileCollection.remove(card);
14665         //this.items = this.items.filter(function(e) { return e != card });
14666         // dont' really need ot update items.
14667         card.el.dom.parentNode.removeChild(card.el.dom);
14668         this.updateInput();
14669
14670         
14671     },
14672     reset: function()
14673     {
14674         this.fileCollection.each(function(card) {
14675             if (card.el.dom && card.el.dom.parentNode) {
14676                 card.el.dom.parentNode.removeChild(card.el.dom);
14677             }
14678         });
14679         this.fileCollection.clear();
14680         this.updateInput();
14681     },
14682     
14683     updateInput : function()
14684     {
14685          var data = [];
14686         this.fileCollection.each(function(e) {
14687             data.push(e.data);
14688             
14689         });
14690         this.inputEl().dom.value = JSON.stringify(data);
14691         
14692         
14693         
14694     }
14695     
14696     
14697 });
14698
14699
14700 Roo.bootstrap.form.CardUploader.ID = -1;/*
14701  * Based on:
14702  * Ext JS Library 1.1.1
14703  * Copyright(c) 2006-2007, Ext JS, LLC.
14704  *
14705  * Originally Released Under LGPL - original licence link has changed is not relivant.
14706  *
14707  * Fork - LGPL
14708  * <script type="text/javascript">
14709  */
14710
14711
14712 /**
14713  * @class Roo.data.SortTypes
14714  * @static
14715  * Defines the default sorting (casting?) comparison functions used when sorting data.
14716  */
14717 Roo.data.SortTypes = {
14718     /**
14719      * Default sort that does nothing
14720      * @param {Mixed} s The value being converted
14721      * @return {Mixed} The comparison value
14722      */
14723     none : function(s){
14724         return s;
14725     },
14726     
14727     /**
14728      * The regular expression used to strip tags
14729      * @type {RegExp}
14730      * @property
14731      */
14732     stripTagsRE : /<\/?[^>]+>/gi,
14733     
14734     /**
14735      * Strips all HTML tags to sort on text only
14736      * @param {Mixed} s The value being converted
14737      * @return {String} The comparison value
14738      */
14739     asText : function(s){
14740         return String(s).replace(this.stripTagsRE, "");
14741     },
14742     
14743     /**
14744      * Strips all HTML tags to sort on text only - Case insensitive
14745      * @param {Mixed} s The value being converted
14746      * @return {String} The comparison value
14747      */
14748     asUCText : function(s){
14749         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14750     },
14751     
14752     /**
14753      * Case insensitive string
14754      * @param {Mixed} s The value being converted
14755      * @return {String} The comparison value
14756      */
14757     asUCString : function(s) {
14758         return String(s).toUpperCase();
14759     },
14760     
14761     /**
14762      * Date sorting
14763      * @param {Mixed} s The value being converted
14764      * @return {Number} The comparison value
14765      */
14766     asDate : function(s) {
14767         if(!s){
14768             return 0;
14769         }
14770         if(s instanceof Date){
14771             return s.getTime();
14772         }
14773         return Date.parse(String(s));
14774     },
14775     
14776     /**
14777      * Float sorting
14778      * @param {Mixed} s The value being converted
14779      * @return {Float} The comparison value
14780      */
14781     asFloat : function(s) {
14782         var val = parseFloat(String(s).replace(/,/g, ""));
14783         if(isNaN(val)) {
14784             val = 0;
14785         }
14786         return val;
14787     },
14788     
14789     /**
14790      * Integer sorting
14791      * @param {Mixed} s The value being converted
14792      * @return {Number} The comparison value
14793      */
14794     asInt : function(s) {
14795         var val = parseInt(String(s).replace(/,/g, ""));
14796         if(isNaN(val)) {
14797             val = 0;
14798         }
14799         return val;
14800     }
14801 };/*
14802  * Based on:
14803  * Ext JS Library 1.1.1
14804  * Copyright(c) 2006-2007, Ext JS, LLC.
14805  *
14806  * Originally Released Under LGPL - original licence link has changed is not relivant.
14807  *
14808  * Fork - LGPL
14809  * <script type="text/javascript">
14810  */
14811
14812 /**
14813 * @class Roo.data.Record
14814  * Instances of this class encapsulate both record <em>definition</em> information, and record
14815  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14816  * to access Records cached in an {@link Roo.data.Store} object.<br>
14817  * <p>
14818  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14819  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14820  * objects.<br>
14821  * <p>
14822  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14823  * @constructor
14824  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14825  * {@link #create}. The parameters are the same.
14826  * @param {Array} data An associative Array of data values keyed by the field name.
14827  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14828  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14829  * not specified an integer id is generated.
14830  */
14831 Roo.data.Record = function(data, id){
14832     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14833     this.data = data;
14834 };
14835
14836 /**
14837  * Generate a constructor for a specific record layout.
14838  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14839  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14840  * Each field definition object may contain the following properties: <ul>
14841  * <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,
14842  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14843  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14844  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14845  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14846  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14847  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14848  * this may be omitted.</p></li>
14849  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14850  * <ul><li>auto (Default, implies no conversion)</li>
14851  * <li>string</li>
14852  * <li>int</li>
14853  * <li>float</li>
14854  * <li>boolean</li>
14855  * <li>date</li></ul></p></li>
14856  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14857  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14858  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14859  * by the Reader into an object that will be stored in the Record. It is passed the
14860  * following parameters:<ul>
14861  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14862  * </ul></p></li>
14863  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14864  * </ul>
14865  * <br>usage:<br><pre><code>
14866 var TopicRecord = Roo.data.Record.create(
14867     {name: 'title', mapping: 'topic_title'},
14868     {name: 'author', mapping: 'username'},
14869     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14870     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14871     {name: 'lastPoster', mapping: 'user2'},
14872     {name: 'excerpt', mapping: 'post_text'}
14873 );
14874
14875 var myNewRecord = new TopicRecord({
14876     title: 'Do my job please',
14877     author: 'noobie',
14878     totalPosts: 1,
14879     lastPost: new Date(),
14880     lastPoster: 'Animal',
14881     excerpt: 'No way dude!'
14882 });
14883 myStore.add(myNewRecord);
14884 </code></pre>
14885  * @method create
14886  * @static
14887  */
14888 Roo.data.Record.create = function(o){
14889     var f = function(){
14890         f.superclass.constructor.apply(this, arguments);
14891     };
14892     Roo.extend(f, Roo.data.Record);
14893     var p = f.prototype;
14894     p.fields = new Roo.util.MixedCollection(false, function(field){
14895         return field.name;
14896     });
14897     for(var i = 0, len = o.length; i < len; i++){
14898         p.fields.add(new Roo.data.Field(o[i]));
14899     }
14900     f.getField = function(name){
14901         return p.fields.get(name);  
14902     };
14903     return f;
14904 };
14905
14906 Roo.data.Record.AUTO_ID = 1000;
14907 Roo.data.Record.EDIT = 'edit';
14908 Roo.data.Record.REJECT = 'reject';
14909 Roo.data.Record.COMMIT = 'commit';
14910
14911 Roo.data.Record.prototype = {
14912     /**
14913      * Readonly flag - true if this record has been modified.
14914      * @type Boolean
14915      */
14916     dirty : false,
14917     editing : false,
14918     error: null,
14919     modified: null,
14920
14921     // private
14922     join : function(store){
14923         this.store = store;
14924     },
14925
14926     /**
14927      * Set the named field to the specified value.
14928      * @param {String} name The name of the field to set.
14929      * @param {Object} value The value to set the field to.
14930      */
14931     set : function(name, value){
14932         if(this.data[name] == value){
14933             return;
14934         }
14935         this.dirty = true;
14936         if(!this.modified){
14937             this.modified = {};
14938         }
14939         if(typeof this.modified[name] == 'undefined'){
14940             this.modified[name] = this.data[name];
14941         }
14942         this.data[name] = value;
14943         if(!this.editing && this.store){
14944             this.store.afterEdit(this);
14945         }       
14946     },
14947
14948     /**
14949      * Get the value of the named field.
14950      * @param {String} name The name of the field to get the value of.
14951      * @return {Object} The value of the field.
14952      */
14953     get : function(name){
14954         return this.data[name]; 
14955     },
14956
14957     // private
14958     beginEdit : function(){
14959         this.editing = true;
14960         this.modified = {}; 
14961     },
14962
14963     // private
14964     cancelEdit : function(){
14965         this.editing = false;
14966         delete this.modified;
14967     },
14968
14969     // private
14970     endEdit : function(){
14971         this.editing = false;
14972         if(this.dirty && this.store){
14973             this.store.afterEdit(this);
14974         }
14975     },
14976
14977     /**
14978      * Usually called by the {@link Roo.data.Store} which owns the Record.
14979      * Rejects all changes made to the Record since either creation, or the last commit operation.
14980      * Modified fields are reverted to their original values.
14981      * <p>
14982      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14983      * of reject operations.
14984      */
14985     reject : function(){
14986         var m = this.modified;
14987         for(var n in m){
14988             if(typeof m[n] != "function"){
14989                 this.data[n] = m[n];
14990             }
14991         }
14992         this.dirty = false;
14993         delete this.modified;
14994         this.editing = false;
14995         if(this.store){
14996             this.store.afterReject(this);
14997         }
14998     },
14999
15000     /**
15001      * Usually called by the {@link Roo.data.Store} which owns the Record.
15002      * Commits all changes made to the Record since either creation, or the last commit operation.
15003      * <p>
15004      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15005      * of commit operations.
15006      */
15007     commit : function(){
15008         this.dirty = false;
15009         delete this.modified;
15010         this.editing = false;
15011         if(this.store){
15012             this.store.afterCommit(this);
15013         }
15014     },
15015
15016     // private
15017     hasError : function(){
15018         return this.error != null;
15019     },
15020
15021     // private
15022     clearError : function(){
15023         this.error = null;
15024     },
15025
15026     /**
15027      * Creates a copy of this record.
15028      * @param {String} id (optional) A new record id if you don't want to use this record's id
15029      * @return {Record}
15030      */
15031     copy : function(newId) {
15032         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15033     }
15034 };/*
15035  * Based on:
15036  * Ext JS Library 1.1.1
15037  * Copyright(c) 2006-2007, Ext JS, LLC.
15038  *
15039  * Originally Released Under LGPL - original licence link has changed is not relivant.
15040  *
15041  * Fork - LGPL
15042  * <script type="text/javascript">
15043  */
15044
15045
15046
15047 /**
15048  * @class Roo.data.Store
15049  * @extends Roo.util.Observable
15050  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15051  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15052  * <p>
15053  * 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
15054  * has no knowledge of the format of the data returned by the Proxy.<br>
15055  * <p>
15056  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15057  * instances from the data object. These records are cached and made available through accessor functions.
15058  * @constructor
15059  * Creates a new Store.
15060  * @param {Object} config A config object containing the objects needed for the Store to access data,
15061  * and read the data into Records.
15062  */
15063 Roo.data.Store = function(config){
15064     this.data = new Roo.util.MixedCollection(false);
15065     this.data.getKey = function(o){
15066         return o.id;
15067     };
15068     this.baseParams = {};
15069     // private
15070     this.paramNames = {
15071         "start" : "start",
15072         "limit" : "limit",
15073         "sort" : "sort",
15074         "dir" : "dir",
15075         "multisort" : "_multisort"
15076     };
15077
15078     if(config && config.data){
15079         this.inlineData = config.data;
15080         delete config.data;
15081     }
15082
15083     Roo.apply(this, config);
15084     
15085     if(this.reader){ // reader passed
15086         this.reader = Roo.factory(this.reader, Roo.data);
15087         this.reader.xmodule = this.xmodule || false;
15088         if(!this.recordType){
15089             this.recordType = this.reader.recordType;
15090         }
15091         if(this.reader.onMetaChange){
15092             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15093         }
15094     }
15095
15096     if(this.recordType){
15097         this.fields = this.recordType.prototype.fields;
15098     }
15099     this.modified = [];
15100
15101     this.addEvents({
15102         /**
15103          * @event datachanged
15104          * Fires when the data cache has changed, and a widget which is using this Store
15105          * as a Record cache should refresh its view.
15106          * @param {Store} this
15107          */
15108         datachanged : true,
15109         /**
15110          * @event metachange
15111          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15112          * @param {Store} this
15113          * @param {Object} meta The JSON metadata
15114          */
15115         metachange : true,
15116         /**
15117          * @event add
15118          * Fires when Records have been added to the Store
15119          * @param {Store} this
15120          * @param {Roo.data.Record[]} records The array of Records added
15121          * @param {Number} index The index at which the record(s) were added
15122          */
15123         add : true,
15124         /**
15125          * @event remove
15126          * Fires when a Record has been removed from the Store
15127          * @param {Store} this
15128          * @param {Roo.data.Record} record The Record that was removed
15129          * @param {Number} index The index at which the record was removed
15130          */
15131         remove : true,
15132         /**
15133          * @event update
15134          * Fires when a Record has been updated
15135          * @param {Store} this
15136          * @param {Roo.data.Record} record The Record that was updated
15137          * @param {String} operation The update operation being performed.  Value may be one of:
15138          * <pre><code>
15139  Roo.data.Record.EDIT
15140  Roo.data.Record.REJECT
15141  Roo.data.Record.COMMIT
15142          * </code></pre>
15143          */
15144         update : true,
15145         /**
15146          * @event clear
15147          * Fires when the data cache has been cleared.
15148          * @param {Store} this
15149          */
15150         clear : true,
15151         /**
15152          * @event beforeload
15153          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15154          * the load action will be canceled.
15155          * @param {Store} this
15156          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15157          */
15158         beforeload : true,
15159         /**
15160          * @event beforeloadadd
15161          * Fires after a new set of Records has been loaded.
15162          * @param {Store} this
15163          * @param {Roo.data.Record[]} records The Records that were loaded
15164          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15165          */
15166         beforeloadadd : true,
15167         /**
15168          * @event load
15169          * Fires after a new set of Records has been loaded, before they are added to the store.
15170          * @param {Store} this
15171          * @param {Roo.data.Record[]} records The Records that were loaded
15172          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15173          * @params {Object} return from reader
15174          */
15175         load : true,
15176         /**
15177          * @event loadexception
15178          * Fires if an exception occurs in the Proxy during loading.
15179          * Called with the signature of the Proxy's "loadexception" event.
15180          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15181          * 
15182          * @param {Proxy} 
15183          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15184          * @param {Object} load options 
15185          * @param {Object} jsonData from your request (normally this contains the Exception)
15186          */
15187         loadexception : true
15188     });
15189     
15190     if(this.proxy){
15191         this.proxy = Roo.factory(this.proxy, Roo.data);
15192         this.proxy.xmodule = this.xmodule || false;
15193         this.relayEvents(this.proxy,  ["loadexception"]);
15194     }
15195     this.sortToggle = {};
15196     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15197
15198     Roo.data.Store.superclass.constructor.call(this);
15199
15200     if(this.inlineData){
15201         this.loadData(this.inlineData);
15202         delete this.inlineData;
15203     }
15204 };
15205
15206 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15207      /**
15208     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15209     * without a remote query - used by combo/forms at present.
15210     */
15211     
15212     /**
15213     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15214     */
15215     /**
15216     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15217     */
15218     /**
15219     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15220     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15221     */
15222     /**
15223     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15224     * on any HTTP request
15225     */
15226     /**
15227     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15228     */
15229     /**
15230     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15231     */
15232     multiSort: false,
15233     /**
15234     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15235     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15236     */
15237     remoteSort : false,
15238
15239     /**
15240     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15241      * loaded or when a record is removed. (defaults to false).
15242     */
15243     pruneModifiedRecords : false,
15244
15245     // private
15246     lastOptions : null,
15247
15248     /**
15249      * Add Records to the Store and fires the add event.
15250      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15251      */
15252     add : function(records){
15253         records = [].concat(records);
15254         for(var i = 0, len = records.length; i < len; i++){
15255             records[i].join(this);
15256         }
15257         var index = this.data.length;
15258         this.data.addAll(records);
15259         this.fireEvent("add", this, records, index);
15260     },
15261
15262     /**
15263      * Remove a Record from the Store and fires the remove event.
15264      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15265      */
15266     remove : function(record){
15267         var index = this.data.indexOf(record);
15268         this.data.removeAt(index);
15269  
15270         if(this.pruneModifiedRecords){
15271             this.modified.remove(record);
15272         }
15273         this.fireEvent("remove", this, record, index);
15274     },
15275
15276     /**
15277      * Remove all Records from the Store and fires the clear event.
15278      */
15279     removeAll : function(){
15280         this.data.clear();
15281         if(this.pruneModifiedRecords){
15282             this.modified = [];
15283         }
15284         this.fireEvent("clear", this);
15285     },
15286
15287     /**
15288      * Inserts Records to the Store at the given index and fires the add event.
15289      * @param {Number} index The start index at which to insert the passed Records.
15290      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15291      */
15292     insert : function(index, records){
15293         records = [].concat(records);
15294         for(var i = 0, len = records.length; i < len; i++){
15295             this.data.insert(index, records[i]);
15296             records[i].join(this);
15297         }
15298         this.fireEvent("add", this, records, index);
15299     },
15300
15301     /**
15302      * Get the index within the cache of the passed Record.
15303      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15304      * @return {Number} The index of the passed Record. Returns -1 if not found.
15305      */
15306     indexOf : function(record){
15307         return this.data.indexOf(record);
15308     },
15309
15310     /**
15311      * Get the index within the cache of the Record with the passed id.
15312      * @param {String} id The id of the Record to find.
15313      * @return {Number} The index of the Record. Returns -1 if not found.
15314      */
15315     indexOfId : function(id){
15316         return this.data.indexOfKey(id);
15317     },
15318
15319     /**
15320      * Get the Record with the specified id.
15321      * @param {String} id The id of the Record to find.
15322      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15323      */
15324     getById : function(id){
15325         return this.data.key(id);
15326     },
15327
15328     /**
15329      * Get the Record at the specified index.
15330      * @param {Number} index The index of the Record to find.
15331      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15332      */
15333     getAt : function(index){
15334         return this.data.itemAt(index);
15335     },
15336
15337     /**
15338      * Returns a range of Records between specified indices.
15339      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15340      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15341      * @return {Roo.data.Record[]} An array of Records
15342      */
15343     getRange : function(start, end){
15344         return this.data.getRange(start, end);
15345     },
15346
15347     // private
15348     storeOptions : function(o){
15349         o = Roo.apply({}, o);
15350         delete o.callback;
15351         delete o.scope;
15352         this.lastOptions = o;
15353     },
15354
15355     /**
15356      * Loads the Record cache from the configured Proxy using the configured Reader.
15357      * <p>
15358      * If using remote paging, then the first load call must specify the <em>start</em>
15359      * and <em>limit</em> properties in the options.params property to establish the initial
15360      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15361      * <p>
15362      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15363      * and this call will return before the new data has been loaded. Perform any post-processing
15364      * in a callback function, or in a "load" event handler.</strong>
15365      * <p>
15366      * @param {Object} options An object containing properties which control loading options:<ul>
15367      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15368      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15369      * <pre>
15370                 {
15371                     data : data,  // array of key=>value data like JsonReader
15372                     total : data.length,
15373                     success : true
15374                     
15375                 }
15376         </pre>
15377             }.</li>
15378      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15379      * passed the following arguments:<ul>
15380      * <li>r : Roo.data.Record[]</li>
15381      * <li>options: Options object from the load call</li>
15382      * <li>success: Boolean success indicator</li></ul></li>
15383      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15384      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15385      * </ul>
15386      */
15387     load : function(options){
15388         options = options || {};
15389         if(this.fireEvent("beforeload", this, options) !== false){
15390             this.storeOptions(options);
15391             var p = Roo.apply(options.params || {}, this.baseParams);
15392             // if meta was not loaded from remote source.. try requesting it.
15393             if (!this.reader.metaFromRemote) {
15394                 p._requestMeta = 1;
15395             }
15396             if(this.sortInfo && this.remoteSort){
15397                 var pn = this.paramNames;
15398                 p[pn["sort"]] = this.sortInfo.field;
15399                 p[pn["dir"]] = this.sortInfo.direction;
15400             }
15401             if (this.multiSort) {
15402                 var pn = this.paramNames;
15403                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15404             }
15405             
15406             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15407         }
15408     },
15409
15410     /**
15411      * Reloads the Record cache from the configured Proxy using the configured Reader and
15412      * the options from the last load operation performed.
15413      * @param {Object} options (optional) An object containing properties which may override the options
15414      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15415      * the most recently used options are reused).
15416      */
15417     reload : function(options){
15418         this.load(Roo.applyIf(options||{}, this.lastOptions));
15419     },
15420
15421     // private
15422     // Called as a callback by the Reader during a load operation.
15423     loadRecords : function(o, options, success){
15424          
15425         if(!o){
15426             if(success !== false){
15427                 this.fireEvent("load", this, [], options, o);
15428             }
15429             if(options.callback){
15430                 options.callback.call(options.scope || this, [], options, false);
15431             }
15432             return;
15433         }
15434         // if data returned failure - throw an exception.
15435         if (o.success === false) {
15436             // show a message if no listener is registered.
15437             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15438                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15439             }
15440             // loadmask wil be hooked into this..
15441             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15442             return;
15443         }
15444         var r = o.records, t = o.totalRecords || r.length;
15445         
15446         this.fireEvent("beforeloadadd", this, r, options, o);
15447         
15448         if(!options || options.add !== true){
15449             if(this.pruneModifiedRecords){
15450                 this.modified = [];
15451             }
15452             for(var i = 0, len = r.length; i < len; i++){
15453                 r[i].join(this);
15454             }
15455             if(this.snapshot){
15456                 this.data = this.snapshot;
15457                 delete this.snapshot;
15458             }
15459             this.data.clear();
15460             this.data.addAll(r);
15461             this.totalLength = t;
15462             this.applySort();
15463             this.fireEvent("datachanged", this);
15464         }else{
15465             this.totalLength = Math.max(t, this.data.length+r.length);
15466             this.add(r);
15467         }
15468         
15469         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15470                 
15471             var e = new Roo.data.Record({});
15472
15473             e.set(this.parent.displayField, this.parent.emptyTitle);
15474             e.set(this.parent.valueField, '');
15475
15476             this.insert(0, e);
15477         }
15478             
15479         this.fireEvent("load", this, r, options, o);
15480         if(options.callback){
15481             options.callback.call(options.scope || this, r, options, true);
15482         }
15483     },
15484
15485
15486     /**
15487      * Loads data from a passed data block. A Reader which understands the format of the data
15488      * must have been configured in the constructor.
15489      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15490      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15491      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15492      */
15493     loadData : function(o, append){
15494         var r = this.reader.readRecords(o);
15495         this.loadRecords(r, {add: append}, true);
15496     },
15497     
15498      /**
15499      * using 'cn' the nested child reader read the child array into it's child stores.
15500      * @param {Object} rec The record with a 'children array
15501      */
15502     loadDataFromChildren : function(rec)
15503     {
15504         this.loadData(this.reader.toLoadData(rec));
15505     },
15506     
15507
15508     /**
15509      * Gets the number of cached records.
15510      * <p>
15511      * <em>If using paging, this may not be the total size of the dataset. If the data object
15512      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15513      * the data set size</em>
15514      */
15515     getCount : function(){
15516         return this.data.length || 0;
15517     },
15518
15519     /**
15520      * Gets the total number of records in the dataset as returned by the server.
15521      * <p>
15522      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15523      * the dataset size</em>
15524      */
15525     getTotalCount : function(){
15526         return this.totalLength || 0;
15527     },
15528
15529     /**
15530      * Returns the sort state of the Store as an object with two properties:
15531      * <pre><code>
15532  field {String} The name of the field by which the Records are sorted
15533  direction {String} The sort order, "ASC" or "DESC"
15534      * </code></pre>
15535      */
15536     getSortState : function(){
15537         return this.sortInfo;
15538     },
15539
15540     // private
15541     applySort : function(){
15542         if(this.sortInfo && !this.remoteSort){
15543             var s = this.sortInfo, f = s.field;
15544             var st = this.fields.get(f).sortType;
15545             var fn = function(r1, r2){
15546                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15547                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15548             };
15549             this.data.sort(s.direction, fn);
15550             if(this.snapshot && this.snapshot != this.data){
15551                 this.snapshot.sort(s.direction, fn);
15552             }
15553         }
15554     },
15555
15556     /**
15557      * Sets the default sort column and order to be used by the next load operation.
15558      * @param {String} fieldName The name of the field to sort by.
15559      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15560      */
15561     setDefaultSort : function(field, dir){
15562         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15563     },
15564
15565     /**
15566      * Sort the Records.
15567      * If remote sorting is used, the sort is performed on the server, and the cache is
15568      * reloaded. If local sorting is used, the cache is sorted internally.
15569      * @param {String} fieldName The name of the field to sort by.
15570      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15571      */
15572     sort : function(fieldName, dir){
15573         var f = this.fields.get(fieldName);
15574         if(!dir){
15575             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15576             
15577             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15578                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15579             }else{
15580                 dir = f.sortDir;
15581             }
15582         }
15583         this.sortToggle[f.name] = dir;
15584         this.sortInfo = {field: f.name, direction: dir};
15585         if(!this.remoteSort){
15586             this.applySort();
15587             this.fireEvent("datachanged", this);
15588         }else{
15589             this.load(this.lastOptions);
15590         }
15591     },
15592
15593     /**
15594      * Calls the specified function for each of the Records in the cache.
15595      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15596      * Returning <em>false</em> aborts and exits the iteration.
15597      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15598      */
15599     each : function(fn, scope){
15600         this.data.each(fn, scope);
15601     },
15602
15603     /**
15604      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15605      * (e.g., during paging).
15606      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15607      */
15608     getModifiedRecords : function(){
15609         return this.modified;
15610     },
15611
15612     // private
15613     createFilterFn : function(property, value, anyMatch){
15614         if(!value.exec){ // not a regex
15615             value = String(value);
15616             if(value.length == 0){
15617                 return false;
15618             }
15619             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15620         }
15621         return function(r){
15622             return value.test(r.data[property]);
15623         };
15624     },
15625
15626     /**
15627      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15628      * @param {String} property A field on your records
15629      * @param {Number} start The record index to start at (defaults to 0)
15630      * @param {Number} end The last record index to include (defaults to length - 1)
15631      * @return {Number} The sum
15632      */
15633     sum : function(property, start, end){
15634         var rs = this.data.items, v = 0;
15635         start = start || 0;
15636         end = (end || end === 0) ? end : rs.length-1;
15637
15638         for(var i = start; i <= end; i++){
15639             v += (rs[i].data[property] || 0);
15640         }
15641         return v;
15642     },
15643
15644     /**
15645      * Filter the records by a specified property.
15646      * @param {String} field A field on your records
15647      * @param {String/RegExp} value Either a string that the field
15648      * should start with or a RegExp to test against the field
15649      * @param {Boolean} anyMatch True to match any part not just the beginning
15650      */
15651     filter : function(property, value, anyMatch){
15652         var fn = this.createFilterFn(property, value, anyMatch);
15653         return fn ? this.filterBy(fn) : this.clearFilter();
15654     },
15655
15656     /**
15657      * Filter by a function. The specified function will be called with each
15658      * record in this data source. If the function returns true the record is included,
15659      * otherwise it is filtered.
15660      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15661      * @param {Object} scope (optional) The scope of the function (defaults to this)
15662      */
15663     filterBy : function(fn, scope){
15664         this.snapshot = this.snapshot || this.data;
15665         this.data = this.queryBy(fn, scope||this);
15666         this.fireEvent("datachanged", this);
15667     },
15668
15669     /**
15670      * Query the records by a specified property.
15671      * @param {String} field A field on your records
15672      * @param {String/RegExp} value Either a string that the field
15673      * should start with or a RegExp to test against the field
15674      * @param {Boolean} anyMatch True to match any part not just the beginning
15675      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15676      */
15677     query : function(property, value, anyMatch){
15678         var fn = this.createFilterFn(property, value, anyMatch);
15679         return fn ? this.queryBy(fn) : this.data.clone();
15680     },
15681
15682     /**
15683      * Query by a function. The specified function will be called with each
15684      * record in this data source. If the function returns true the record is included
15685      * in the results.
15686      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15687      * @param {Object} scope (optional) The scope of the function (defaults to this)
15688       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15689      **/
15690     queryBy : function(fn, scope){
15691         var data = this.snapshot || this.data;
15692         return data.filterBy(fn, scope||this);
15693     },
15694
15695     /**
15696      * Collects unique values for a particular dataIndex from this store.
15697      * @param {String} dataIndex The property to collect
15698      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15699      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15700      * @return {Array} An array of the unique values
15701      **/
15702     collect : function(dataIndex, allowNull, bypassFilter){
15703         var d = (bypassFilter === true && this.snapshot) ?
15704                 this.snapshot.items : this.data.items;
15705         var v, sv, r = [], l = {};
15706         for(var i = 0, len = d.length; i < len; i++){
15707             v = d[i].data[dataIndex];
15708             sv = String(v);
15709             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15710                 l[sv] = true;
15711                 r[r.length] = v;
15712             }
15713         }
15714         return r;
15715     },
15716
15717     /**
15718      * Revert to a view of the Record cache with no filtering applied.
15719      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15720      */
15721     clearFilter : function(suppressEvent){
15722         if(this.snapshot && this.snapshot != this.data){
15723             this.data = this.snapshot;
15724             delete this.snapshot;
15725             if(suppressEvent !== true){
15726                 this.fireEvent("datachanged", this);
15727             }
15728         }
15729     },
15730
15731     // private
15732     afterEdit : function(record){
15733         if(this.modified.indexOf(record) == -1){
15734             this.modified.push(record);
15735         }
15736         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15737     },
15738     
15739     // private
15740     afterReject : function(record){
15741         this.modified.remove(record);
15742         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15743     },
15744
15745     // private
15746     afterCommit : function(record){
15747         this.modified.remove(record);
15748         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15749     },
15750
15751     /**
15752      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15753      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15754      */
15755     commitChanges : function(){
15756         var m = this.modified.slice(0);
15757         this.modified = [];
15758         for(var i = 0, len = m.length; i < len; i++){
15759             m[i].commit();
15760         }
15761     },
15762
15763     /**
15764      * Cancel outstanding changes on all changed records.
15765      */
15766     rejectChanges : function(){
15767         var m = this.modified.slice(0);
15768         this.modified = [];
15769         for(var i = 0, len = m.length; i < len; i++){
15770             m[i].reject();
15771         }
15772     },
15773
15774     onMetaChange : function(meta, rtype, o){
15775         this.recordType = rtype;
15776         this.fields = rtype.prototype.fields;
15777         delete this.snapshot;
15778         this.sortInfo = meta.sortInfo || this.sortInfo;
15779         this.modified = [];
15780         this.fireEvent('metachange', this, this.reader.meta);
15781     },
15782     
15783     moveIndex : function(data, type)
15784     {
15785         var index = this.indexOf(data);
15786         
15787         var newIndex = index + type;
15788         
15789         this.remove(data);
15790         
15791         this.insert(newIndex, data);
15792         
15793     }
15794 });/*
15795  * Based on:
15796  * Ext JS Library 1.1.1
15797  * Copyright(c) 2006-2007, Ext JS, LLC.
15798  *
15799  * Originally Released Under LGPL - original licence link has changed is not relivant.
15800  *
15801  * Fork - LGPL
15802  * <script type="text/javascript">
15803  */
15804
15805 /**
15806  * @class Roo.data.SimpleStore
15807  * @extends Roo.data.Store
15808  * Small helper class to make creating Stores from Array data easier.
15809  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15810  * @cfg {Array} fields An array of field definition objects, or field name strings.
15811  * @cfg {Object} an existing reader (eg. copied from another store)
15812  * @cfg {Array} data The multi-dimensional array of data
15813  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15814  * @cfg {Roo.data.Reader} reader  [not-required] 
15815  * @constructor
15816  * @param {Object} config
15817  */
15818 Roo.data.SimpleStore = function(config)
15819 {
15820     Roo.data.SimpleStore.superclass.constructor.call(this, {
15821         isLocal : true,
15822         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15823                 id: config.id
15824             },
15825             Roo.data.Record.create(config.fields)
15826         ),
15827         proxy : new Roo.data.MemoryProxy(config.data)
15828     });
15829     this.load();
15830 };
15831 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15832  * Based on:
15833  * Ext JS Library 1.1.1
15834  * Copyright(c) 2006-2007, Ext JS, LLC.
15835  *
15836  * Originally Released Under LGPL - original licence link has changed is not relivant.
15837  *
15838  * Fork - LGPL
15839  * <script type="text/javascript">
15840  */
15841
15842 /**
15843 /**
15844  * @extends Roo.data.Store
15845  * @class Roo.data.JsonStore
15846  * Small helper class to make creating Stores for JSON data easier. <br/>
15847 <pre><code>
15848 var store = new Roo.data.JsonStore({
15849     url: 'get-images.php',
15850     root: 'images',
15851     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15852 });
15853 </code></pre>
15854  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15855  * JsonReader and HttpProxy (unless inline data is provided).</b>
15856  * @cfg {Array} fields An array of field definition objects, or field name strings.
15857  * @constructor
15858  * @param {Object} config
15859  */
15860 Roo.data.JsonStore = function(c){
15861     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15862         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15863         reader: new Roo.data.JsonReader(c, c.fields)
15864     }));
15865 };
15866 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15867  * Based on:
15868  * Ext JS Library 1.1.1
15869  * Copyright(c) 2006-2007, Ext JS, LLC.
15870  *
15871  * Originally Released Under LGPL - original licence link has changed is not relivant.
15872  *
15873  * Fork - LGPL
15874  * <script type="text/javascript">
15875  */
15876
15877  
15878 Roo.data.Field = function(config){
15879     if(typeof config == "string"){
15880         config = {name: config};
15881     }
15882     Roo.apply(this, config);
15883     
15884     if(!this.type){
15885         this.type = "auto";
15886     }
15887     
15888     var st = Roo.data.SortTypes;
15889     // named sortTypes are supported, here we look them up
15890     if(typeof this.sortType == "string"){
15891         this.sortType = st[this.sortType];
15892     }
15893     
15894     // set default sortType for strings and dates
15895     if(!this.sortType){
15896         switch(this.type){
15897             case "string":
15898                 this.sortType = st.asUCString;
15899                 break;
15900             case "date":
15901                 this.sortType = st.asDate;
15902                 break;
15903             default:
15904                 this.sortType = st.none;
15905         }
15906     }
15907
15908     // define once
15909     var stripRe = /[\$,%]/g;
15910
15911     // prebuilt conversion function for this field, instead of
15912     // switching every time we're reading a value
15913     if(!this.convert){
15914         var cv, dateFormat = this.dateFormat;
15915         switch(this.type){
15916             case "":
15917             case "auto":
15918             case undefined:
15919                 cv = function(v){ return v; };
15920                 break;
15921             case "string":
15922                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15923                 break;
15924             case "int":
15925                 cv = function(v){
15926                     return v !== undefined && v !== null && v !== '' ?
15927                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15928                     };
15929                 break;
15930             case "float":
15931                 cv = function(v){
15932                     return v !== undefined && v !== null && v !== '' ?
15933                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15934                     };
15935                 break;
15936             case "bool":
15937             case "boolean":
15938                 cv = function(v){ return v === true || v === "true" || v == 1; };
15939                 break;
15940             case "date":
15941                 cv = function(v){
15942                     if(!v){
15943                         return '';
15944                     }
15945                     if(v instanceof Date){
15946                         return v;
15947                     }
15948                     if(dateFormat){
15949                         if(dateFormat == "timestamp"){
15950                             return new Date(v*1000);
15951                         }
15952                         return Date.parseDate(v, dateFormat);
15953                     }
15954                     var parsed = Date.parse(v);
15955                     return parsed ? new Date(parsed) : null;
15956                 };
15957              break;
15958             
15959         }
15960         this.convert = cv;
15961     }
15962 };
15963
15964 Roo.data.Field.prototype = {
15965     dateFormat: null,
15966     defaultValue: "",
15967     mapping: null,
15968     sortType : null,
15969     sortDir : "ASC"
15970 };/*
15971  * Based on:
15972  * Ext JS Library 1.1.1
15973  * Copyright(c) 2006-2007, Ext JS, LLC.
15974  *
15975  * Originally Released Under LGPL - original licence link has changed is not relivant.
15976  *
15977  * Fork - LGPL
15978  * <script type="text/javascript">
15979  */
15980  
15981 // Base class for reading structured data from a data source.  This class is intended to be
15982 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15983
15984 /**
15985  * @class Roo.data.DataReader
15986  * @abstract
15987  * Base class for reading structured data from a data source.  This class is intended to be
15988  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15989  */
15990
15991 Roo.data.DataReader = function(meta, recordType){
15992     
15993     this.meta = meta;
15994     
15995     this.recordType = recordType instanceof Array ? 
15996         Roo.data.Record.create(recordType) : recordType;
15997 };
15998
15999 Roo.data.DataReader.prototype = {
16000     
16001     
16002     readerType : 'Data',
16003      /**
16004      * Create an empty record
16005      * @param {Object} data (optional) - overlay some values
16006      * @return {Roo.data.Record} record created.
16007      */
16008     newRow :  function(d) {
16009         var da =  {};
16010         this.recordType.prototype.fields.each(function(c) {
16011             switch( c.type) {
16012                 case 'int' : da[c.name] = 0; break;
16013                 case 'date' : da[c.name] = new Date(); break;
16014                 case 'float' : da[c.name] = 0.0; break;
16015                 case 'boolean' : da[c.name] = false; break;
16016                 default : da[c.name] = ""; break;
16017             }
16018             
16019         });
16020         return new this.recordType(Roo.apply(da, d));
16021     }
16022     
16023     
16024 };/*
16025  * Based on:
16026  * Ext JS Library 1.1.1
16027  * Copyright(c) 2006-2007, Ext JS, LLC.
16028  *
16029  * Originally Released Under LGPL - original licence link has changed is not relivant.
16030  *
16031  * Fork - LGPL
16032  * <script type="text/javascript">
16033  */
16034
16035 /**
16036  * @class Roo.data.DataProxy
16037  * @extends Roo.util.Observable
16038  * @abstract
16039  * This class is an abstract base class for implementations which provide retrieval of
16040  * unformatted data objects.<br>
16041  * <p>
16042  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16043  * (of the appropriate type which knows how to parse the data object) to provide a block of
16044  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16045  * <p>
16046  * Custom implementations must implement the load method as described in
16047  * {@link Roo.data.HttpProxy#load}.
16048  */
16049 Roo.data.DataProxy = function(){
16050     this.addEvents({
16051         /**
16052          * @event beforeload
16053          * Fires before a network request is made to retrieve a data object.
16054          * @param {Object} This DataProxy object.
16055          * @param {Object} params The params parameter to the load function.
16056          */
16057         beforeload : true,
16058         /**
16059          * @event load
16060          * Fires before the load method's callback is called.
16061          * @param {Object} This DataProxy object.
16062          * @param {Object} o The data object.
16063          * @param {Object} arg The callback argument object passed to the load function.
16064          */
16065         load : true,
16066         /**
16067          * @event loadexception
16068          * Fires if an Exception occurs during data retrieval.
16069          * @param {Object} This DataProxy object.
16070          * @param {Object} o The data object.
16071          * @param {Object} arg The callback argument object passed to the load function.
16072          * @param {Object} e The Exception.
16073          */
16074         loadexception : true
16075     });
16076     Roo.data.DataProxy.superclass.constructor.call(this);
16077 };
16078
16079 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16080
16081     /**
16082      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16083      */
16084 /*
16085  * Based on:
16086  * Ext JS Library 1.1.1
16087  * Copyright(c) 2006-2007, Ext JS, LLC.
16088  *
16089  * Originally Released Under LGPL - original licence link has changed is not relivant.
16090  *
16091  * Fork - LGPL
16092  * <script type="text/javascript">
16093  */
16094 /**
16095  * @class Roo.data.MemoryProxy
16096  * @extends Roo.data.DataProxy
16097  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16098  * to the Reader when its load method is called.
16099  * @constructor
16100  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16101  */
16102 Roo.data.MemoryProxy = function(config){
16103     var data = config;
16104     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16105         data = config.data;
16106     }
16107     Roo.data.MemoryProxy.superclass.constructor.call(this);
16108     this.data = data;
16109 };
16110
16111 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16112     
16113     /**
16114      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16115      */
16116     /**
16117      * Load data from the requested source (in this case an in-memory
16118      * data object passed to the constructor), read the data object into
16119      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16120      * process that block using the passed callback.
16121      * @param {Object} params This parameter is not used by the MemoryProxy class.
16122      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16123      * object into a block of Roo.data.Records.
16124      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16125      * The function must be passed <ul>
16126      * <li>The Record block object</li>
16127      * <li>The "arg" argument from the load function</li>
16128      * <li>A boolean success indicator</li>
16129      * </ul>
16130      * @param {Object} scope The scope in which to call the callback
16131      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16132      */
16133     load : function(params, reader, callback, scope, arg){
16134         params = params || {};
16135         var result;
16136         try {
16137             result = reader.readRecords(params.data ? params.data :this.data);
16138         }catch(e){
16139             this.fireEvent("loadexception", this, arg, null, e);
16140             callback.call(scope, null, arg, false);
16141             return;
16142         }
16143         callback.call(scope, result, arg, true);
16144     },
16145     
16146     // private
16147     update : function(params, records){
16148         
16149     }
16150 });/*
16151  * Based on:
16152  * Ext JS Library 1.1.1
16153  * Copyright(c) 2006-2007, Ext JS, LLC.
16154  *
16155  * Originally Released Under LGPL - original licence link has changed is not relivant.
16156  *
16157  * Fork - LGPL
16158  * <script type="text/javascript">
16159  */
16160 /**
16161  * @class Roo.data.HttpProxy
16162  * @extends Roo.data.DataProxy
16163  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16164  * configured to reference a certain URL.<br><br>
16165  * <p>
16166  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16167  * from which the running page was served.<br><br>
16168  * <p>
16169  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16170  * <p>
16171  * Be aware that to enable the browser to parse an XML document, the server must set
16172  * the Content-Type header in the HTTP response to "text/xml".
16173  * @constructor
16174  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16175  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16176  * will be used to make the request.
16177  */
16178 Roo.data.HttpProxy = function(conn){
16179     Roo.data.HttpProxy.superclass.constructor.call(this);
16180     // is conn a conn config or a real conn?
16181     this.conn = conn;
16182     this.useAjax = !conn || !conn.events;
16183   
16184 };
16185
16186 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16187     // thse are take from connection...
16188     
16189     /**
16190      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16191      */
16192     /**
16193      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16194      * extra parameters to each request made by this object. (defaults to undefined)
16195      */
16196     /**
16197      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16198      *  to each request made by this object. (defaults to undefined)
16199      */
16200     /**
16201      * @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)
16202      */
16203     /**
16204      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16205      */
16206      /**
16207      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16208      * @type Boolean
16209      */
16210   
16211
16212     /**
16213      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16214      * @type Boolean
16215      */
16216     /**
16217      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16218      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16219      * a finer-grained basis than the DataProxy events.
16220      */
16221     getConnection : function(){
16222         return this.useAjax ? Roo.Ajax : this.conn;
16223     },
16224
16225     /**
16226      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16227      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16228      * process that block using the passed callback.
16229      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16230      * for the request to the remote server.
16231      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16232      * object into a block of Roo.data.Records.
16233      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16234      * The function must be passed <ul>
16235      * <li>The Record block object</li>
16236      * <li>The "arg" argument from the load function</li>
16237      * <li>A boolean success indicator</li>
16238      * </ul>
16239      * @param {Object} scope The scope in which to call the callback
16240      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16241      */
16242     load : function(params, reader, callback, scope, arg){
16243         if(this.fireEvent("beforeload", this, params) !== false){
16244             var  o = {
16245                 params : params || {},
16246                 request: {
16247                     callback : callback,
16248                     scope : scope,
16249                     arg : arg
16250                 },
16251                 reader: reader,
16252                 callback : this.loadResponse,
16253                 scope: this
16254             };
16255             if(this.useAjax){
16256                 Roo.applyIf(o, this.conn);
16257                 if(this.activeRequest){
16258                     Roo.Ajax.abort(this.activeRequest);
16259                 }
16260                 this.activeRequest = Roo.Ajax.request(o);
16261             }else{
16262                 this.conn.request(o);
16263             }
16264         }else{
16265             callback.call(scope||this, null, arg, false);
16266         }
16267     },
16268
16269     // private
16270     loadResponse : function(o, success, response){
16271         delete this.activeRequest;
16272         if(!success){
16273             this.fireEvent("loadexception", this, o, response);
16274             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16275             return;
16276         }
16277         var result;
16278         try {
16279             result = o.reader.read(response);
16280         }catch(e){
16281             o.success = false;
16282             o.raw = { errorMsg : response.responseText };
16283             this.fireEvent("loadexception", this, o, response, e);
16284             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16285             return;
16286         }
16287         
16288         this.fireEvent("load", this, o, o.request.arg);
16289         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16290     },
16291
16292     // private
16293     update : function(dataSet){
16294
16295     },
16296
16297     // private
16298     updateResponse : function(dataSet){
16299
16300     }
16301 });/*
16302  * Based on:
16303  * Ext JS Library 1.1.1
16304  * Copyright(c) 2006-2007, Ext JS, LLC.
16305  *
16306  * Originally Released Under LGPL - original licence link has changed is not relivant.
16307  *
16308  * Fork - LGPL
16309  * <script type="text/javascript">
16310  */
16311
16312 /**
16313  * @class Roo.data.ScriptTagProxy
16314  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16315  * other than the originating domain of the running page.<br><br>
16316  * <p>
16317  * <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
16318  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16319  * <p>
16320  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16321  * source code that is used as the source inside a &lt;script> tag.<br><br>
16322  * <p>
16323  * In order for the browser to process the returned data, the server must wrap the data object
16324  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16325  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16326  * depending on whether the callback name was passed:
16327  * <p>
16328  * <pre><code>
16329 boolean scriptTag = false;
16330 String cb = request.getParameter("callback");
16331 if (cb != null) {
16332     scriptTag = true;
16333     response.setContentType("text/javascript");
16334 } else {
16335     response.setContentType("application/x-json");
16336 }
16337 Writer out = response.getWriter();
16338 if (scriptTag) {
16339     out.write(cb + "(");
16340 }
16341 out.print(dataBlock.toJsonString());
16342 if (scriptTag) {
16343     out.write(");");
16344 }
16345 </pre></code>
16346  *
16347  * @constructor
16348  * @param {Object} config A configuration object.
16349  */
16350 Roo.data.ScriptTagProxy = function(config){
16351     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16352     Roo.apply(this, config);
16353     this.head = document.getElementsByTagName("head")[0];
16354 };
16355
16356 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16357
16358 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16359     /**
16360      * @cfg {String} url The URL from which to request the data object.
16361      */
16362     /**
16363      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16364      */
16365     timeout : 30000,
16366     /**
16367      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16368      * the server the name of the callback function set up by the load call to process the returned data object.
16369      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16370      * javascript output which calls this named function passing the data object as its only parameter.
16371      */
16372     callbackParam : "callback",
16373     /**
16374      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16375      * name to the request.
16376      */
16377     nocache : true,
16378
16379     /**
16380      * Load data from the configured URL, read the data object into
16381      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16382      * process that block using the passed callback.
16383      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16384      * for the request to the remote server.
16385      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16386      * object into a block of Roo.data.Records.
16387      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16388      * The function must be passed <ul>
16389      * <li>The Record block object</li>
16390      * <li>The "arg" argument from the load function</li>
16391      * <li>A boolean success indicator</li>
16392      * </ul>
16393      * @param {Object} scope The scope in which to call the callback
16394      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16395      */
16396     load : function(params, reader, callback, scope, arg){
16397         if(this.fireEvent("beforeload", this, params) !== false){
16398
16399             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16400
16401             var url = this.url;
16402             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16403             if(this.nocache){
16404                 url += "&_dc=" + (new Date().getTime());
16405             }
16406             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16407             var trans = {
16408                 id : transId,
16409                 cb : "stcCallback"+transId,
16410                 scriptId : "stcScript"+transId,
16411                 params : params,
16412                 arg : arg,
16413                 url : url,
16414                 callback : callback,
16415                 scope : scope,
16416                 reader : reader
16417             };
16418             var conn = this;
16419
16420             window[trans.cb] = function(o){
16421                 conn.handleResponse(o, trans);
16422             };
16423
16424             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16425
16426             if(this.autoAbort !== false){
16427                 this.abort();
16428             }
16429
16430             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16431
16432             var script = document.createElement("script");
16433             script.setAttribute("src", url);
16434             script.setAttribute("type", "text/javascript");
16435             script.setAttribute("id", trans.scriptId);
16436             this.head.appendChild(script);
16437
16438             this.trans = trans;
16439         }else{
16440             callback.call(scope||this, null, arg, false);
16441         }
16442     },
16443
16444     // private
16445     isLoading : function(){
16446         return this.trans ? true : false;
16447     },
16448
16449     /**
16450      * Abort the current server request.
16451      */
16452     abort : function(){
16453         if(this.isLoading()){
16454             this.destroyTrans(this.trans);
16455         }
16456     },
16457
16458     // private
16459     destroyTrans : function(trans, isLoaded){
16460         this.head.removeChild(document.getElementById(trans.scriptId));
16461         clearTimeout(trans.timeoutId);
16462         if(isLoaded){
16463             window[trans.cb] = undefined;
16464             try{
16465                 delete window[trans.cb];
16466             }catch(e){}
16467         }else{
16468             // if hasn't been loaded, wait for load to remove it to prevent script error
16469             window[trans.cb] = function(){
16470                 window[trans.cb] = undefined;
16471                 try{
16472                     delete window[trans.cb];
16473                 }catch(e){}
16474             };
16475         }
16476     },
16477
16478     // private
16479     handleResponse : function(o, trans){
16480         this.trans = false;
16481         this.destroyTrans(trans, true);
16482         var result;
16483         try {
16484             result = trans.reader.readRecords(o);
16485         }catch(e){
16486             this.fireEvent("loadexception", this, o, trans.arg, e);
16487             trans.callback.call(trans.scope||window, null, trans.arg, false);
16488             return;
16489         }
16490         this.fireEvent("load", this, o, trans.arg);
16491         trans.callback.call(trans.scope||window, result, trans.arg, true);
16492     },
16493
16494     // private
16495     handleFailure : function(trans){
16496         this.trans = false;
16497         this.destroyTrans(trans, false);
16498         this.fireEvent("loadexception", this, null, trans.arg);
16499         trans.callback.call(trans.scope||window, null, trans.arg, false);
16500     }
16501 });/*
16502  * Based on:
16503  * Ext JS Library 1.1.1
16504  * Copyright(c) 2006-2007, Ext JS, LLC.
16505  *
16506  * Originally Released Under LGPL - original licence link has changed is not relivant.
16507  *
16508  * Fork - LGPL
16509  * <script type="text/javascript">
16510  */
16511
16512 /**
16513  * @class Roo.data.JsonReader
16514  * @extends Roo.data.DataReader
16515  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16516  * based on mappings in a provided Roo.data.Record constructor.
16517  * 
16518  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16519  * in the reply previously. 
16520  * 
16521  * <p>
16522  * Example code:
16523  * <pre><code>
16524 var RecordDef = Roo.data.Record.create([
16525     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16526     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16527 ]);
16528 var myReader = new Roo.data.JsonReader({
16529     totalProperty: "results",    // The property which contains the total dataset size (optional)
16530     root: "rows",                // The property which contains an Array of row objects
16531     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16532 }, RecordDef);
16533 </code></pre>
16534  * <p>
16535  * This would consume a JSON file like this:
16536  * <pre><code>
16537 { 'results': 2, 'rows': [
16538     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16539     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16540 }
16541 </code></pre>
16542  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16543  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16544  * paged from the remote server.
16545  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16546  * @cfg {String} root name of the property which contains the Array of row objects.
16547  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16548  * @cfg {Array} fields Array of field definition objects
16549  * @constructor
16550  * Create a new JsonReader
16551  * @param {Object} meta Metadata configuration options
16552  * @param {Object} recordType Either an Array of field definition objects,
16553  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16554  */
16555 Roo.data.JsonReader = function(meta, recordType){
16556     
16557     meta = meta || {};
16558     // set some defaults:
16559     Roo.applyIf(meta, {
16560         totalProperty: 'total',
16561         successProperty : 'success',
16562         root : 'data',
16563         id : 'id'
16564     });
16565     
16566     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16567 };
16568 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16569     
16570     readerType : 'Json',
16571     
16572     /**
16573      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16574      * Used by Store query builder to append _requestMeta to params.
16575      * 
16576      */
16577     metaFromRemote : false,
16578     /**
16579      * This method is only used by a DataProxy which has retrieved data from a remote server.
16580      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16581      * @return {Object} data A data block which is used by an Roo.data.Store object as
16582      * a cache of Roo.data.Records.
16583      */
16584     read : function(response){
16585         var json = response.responseText;
16586        
16587         var o = /* eval:var:o */ eval("("+json+")");
16588         if(!o) {
16589             throw {message: "JsonReader.read: Json object not found"};
16590         }
16591         
16592         if(o.metaData){
16593             
16594             delete this.ef;
16595             this.metaFromRemote = true;
16596             this.meta = o.metaData;
16597             this.recordType = Roo.data.Record.create(o.metaData.fields);
16598             this.onMetaChange(this.meta, this.recordType, o);
16599         }
16600         return this.readRecords(o);
16601     },
16602
16603     // private function a store will implement
16604     onMetaChange : function(meta, recordType, o){
16605
16606     },
16607
16608     /**
16609          * @ignore
16610          */
16611     simpleAccess: function(obj, subsc) {
16612         return obj[subsc];
16613     },
16614
16615         /**
16616          * @ignore
16617          */
16618     getJsonAccessor: function(){
16619         var re = /[\[\.]/;
16620         return function(expr) {
16621             try {
16622                 return(re.test(expr))
16623                     ? new Function("obj", "return obj." + expr)
16624                     : function(obj){
16625                         return obj[expr];
16626                     };
16627             } catch(e){}
16628             return Roo.emptyFn;
16629         };
16630     }(),
16631
16632     /**
16633      * Create a data block containing Roo.data.Records from an XML document.
16634      * @param {Object} o An object which contains an Array of row objects in the property specified
16635      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16636      * which contains the total size of the dataset.
16637      * @return {Object} data A data block which is used by an Roo.data.Store object as
16638      * a cache of Roo.data.Records.
16639      */
16640     readRecords : function(o){
16641         /**
16642          * After any data loads, the raw JSON data is available for further custom processing.
16643          * @type Object
16644          */
16645         this.o = o;
16646         var s = this.meta, Record = this.recordType,
16647             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16648
16649 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16650         if (!this.ef) {
16651             if(s.totalProperty) {
16652                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16653                 }
16654                 if(s.successProperty) {
16655                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16656                 }
16657                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16658                 if (s.id) {
16659                         var g = this.getJsonAccessor(s.id);
16660                         this.getId = function(rec) {
16661                                 var r = g(rec);  
16662                                 return (r === undefined || r === "") ? null : r;
16663                         };
16664                 } else {
16665                         this.getId = function(){return null;};
16666                 }
16667             this.ef = [];
16668             for(var jj = 0; jj < fl; jj++){
16669                 f = fi[jj];
16670                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16671                 this.ef[jj] = this.getJsonAccessor(map);
16672             }
16673         }
16674
16675         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16676         if(s.totalProperty){
16677             var vt = parseInt(this.getTotal(o), 10);
16678             if(!isNaN(vt)){
16679                 totalRecords = vt;
16680             }
16681         }
16682         if(s.successProperty){
16683             var vs = this.getSuccess(o);
16684             if(vs === false || vs === 'false'){
16685                 success = false;
16686             }
16687         }
16688         var records = [];
16689         for(var i = 0; i < c; i++){
16690             var n = root[i];
16691             var values = {};
16692             var id = this.getId(n);
16693             for(var j = 0; j < fl; j++){
16694                 f = fi[j];
16695                                 var v = this.ef[j](n);
16696                                 if (!f.convert) {
16697                                         Roo.log('missing convert for ' + f.name);
16698                                         Roo.log(f);
16699                                         continue;
16700                                 }
16701                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16702             }
16703                         if (!Record) {
16704                                 return {
16705                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16706                                         success : false,
16707                                         records : [],
16708                                         totalRecords : 0
16709                                 };
16710                         }
16711             var record = new Record(values, id);
16712             record.json = n;
16713             records[i] = record;
16714         }
16715         return {
16716             raw : o,
16717             success : success,
16718             records : records,
16719             totalRecords : totalRecords
16720         };
16721     },
16722     // used when loading children.. @see loadDataFromChildren
16723     toLoadData: function(rec)
16724     {
16725         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16726         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16727         return { data : data, total : data.length };
16728         
16729     }
16730 });/*
16731  * Based on:
16732  * Ext JS Library 1.1.1
16733  * Copyright(c) 2006-2007, Ext JS, LLC.
16734  *
16735  * Originally Released Under LGPL - original licence link has changed is not relivant.
16736  *
16737  * Fork - LGPL
16738  * <script type="text/javascript">
16739  */
16740
16741 /**
16742  * @class Roo.data.ArrayReader
16743  * @extends Roo.data.DataReader
16744  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16745  * Each element of that Array represents a row of data fields. The
16746  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16747  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16748  * <p>
16749  * Example code:.
16750  * <pre><code>
16751 var RecordDef = Roo.data.Record.create([
16752     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16753     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16754 ]);
16755 var myReader = new Roo.data.ArrayReader({
16756     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16757 }, RecordDef);
16758 </code></pre>
16759  * <p>
16760  * This would consume an Array like this:
16761  * <pre><code>
16762 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16763   </code></pre>
16764  
16765  * @constructor
16766  * Create a new JsonReader
16767  * @param {Object} meta Metadata configuration options.
16768  * @param {Object|Array} recordType Either an Array of field definition objects
16769  * 
16770  * @cfg {Array} fields Array of field definition objects
16771  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16772  * as specified to {@link Roo.data.Record#create},
16773  * or an {@link Roo.data.Record} object
16774  *
16775  * 
16776  * created using {@link Roo.data.Record#create}.
16777  */
16778 Roo.data.ArrayReader = function(meta, recordType)
16779 {    
16780     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16781 };
16782
16783 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16784     
16785       /**
16786      * Create a data block containing Roo.data.Records from an XML document.
16787      * @param {Object} o An Array of row objects which represents the dataset.
16788      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16789      * a cache of Roo.data.Records.
16790      */
16791     readRecords : function(o)
16792     {
16793         var sid = this.meta ? this.meta.id : null;
16794         var recordType = this.recordType, fields = recordType.prototype.fields;
16795         var records = [];
16796         var root = o;
16797         for(var i = 0; i < root.length; i++){
16798             var n = root[i];
16799             var values = {};
16800             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16801             for(var j = 0, jlen = fields.length; j < jlen; j++){
16802                 var f = fields.items[j];
16803                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16804                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16805                 v = f.convert(v);
16806                 values[f.name] = v;
16807             }
16808             var record = new recordType(values, id);
16809             record.json = n;
16810             records[records.length] = record;
16811         }
16812         return {
16813             records : records,
16814             totalRecords : records.length
16815         };
16816     },
16817     // used when loading children.. @see loadDataFromChildren
16818     toLoadData: function(rec)
16819     {
16820         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16821         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16822         
16823     }
16824     
16825     
16826 });/*
16827  * - LGPL
16828  * * 
16829  */
16830
16831 /**
16832  * @class Roo.bootstrap.form.ComboBox
16833  * @extends Roo.bootstrap.form.TriggerField
16834  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16835  * @cfg {Boolean} append (true|false) default false
16836  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16837  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16838  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16839  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16840  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16841  * @cfg {Boolean} animate default true
16842  * @cfg {Boolean} emptyResultText only for touch device
16843  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16844  * @cfg {String} emptyTitle default ''
16845  * @cfg {Number} width fixed with? experimental
16846  * @constructor
16847  * Create a new ComboBox.
16848  * @param {Object} config Configuration options
16849  */
16850 Roo.bootstrap.form.ComboBox = function(config){
16851     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16852     this.addEvents({
16853         /**
16854          * @event expand
16855          * Fires when the dropdown list is expanded
16856         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16857         */
16858         'expand' : true,
16859         /**
16860          * @event collapse
16861          * Fires when the dropdown list is collapsed
16862         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16863         */
16864         'collapse' : true,
16865         /**
16866          * @event beforeselect
16867          * Fires before a list item is selected. Return false to cancel the selection.
16868         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16869         * @param {Roo.data.Record} record The data record returned from the underlying store
16870         * @param {Number} index The index of the selected item in the dropdown list
16871         */
16872         'beforeselect' : true,
16873         /**
16874          * @event select
16875          * Fires when a list item is selected
16876         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16877         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16878         * @param {Number} index The index of the selected item in the dropdown list
16879         */
16880         'select' : true,
16881         /**
16882          * @event beforequery
16883          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16884          * The event object passed has these properties:
16885         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16886         * @param {String} query The query
16887         * @param {Boolean} forceAll true to force "all" query
16888         * @param {Boolean} cancel true to cancel the query
16889         * @param {Object} e The query event object
16890         */
16891         'beforequery': true,
16892          /**
16893          * @event add
16894          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16895         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16896         */
16897         'add' : true,
16898         /**
16899          * @event edit
16900          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16901         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16902         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16903         */
16904         'edit' : true,
16905         /**
16906          * @event remove
16907          * Fires when the remove value from the combobox array
16908         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16909         */
16910         'remove' : true,
16911         /**
16912          * @event afterremove
16913          * Fires when the remove value from the combobox array
16914         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16915         */
16916         'afterremove' : true,
16917         /**
16918          * @event specialfilter
16919          * Fires when specialfilter
16920             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16921             */
16922         'specialfilter' : true,
16923         /**
16924          * @event tick
16925          * Fires when tick the element
16926             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16927             */
16928         'tick' : true,
16929         /**
16930          * @event touchviewdisplay
16931          * Fires when touch view require special display (default is using displayField)
16932             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16933             * @param {Object} cfg set html .
16934             */
16935         'touchviewdisplay' : true
16936         
16937     });
16938     
16939     this.item = [];
16940     this.tickItems = [];
16941     
16942     this.selectedIndex = -1;
16943     if(this.mode == 'local'){
16944         if(config.queryDelay === undefined){
16945             this.queryDelay = 10;
16946         }
16947         if(config.minChars === undefined){
16948             this.minChars = 0;
16949         }
16950     }
16951 };
16952
16953 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16954      
16955     /**
16956      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16957      * rendering into an Roo.Editor, defaults to false)
16958      */
16959     /**
16960      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16961      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16962      */
16963     /**
16964      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16965      */
16966     /**
16967      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16968      * the dropdown list (defaults to undefined, with no header element)
16969      */
16970
16971      /**
16972      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16973      */
16974      
16975      /**
16976      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16977      */
16978     listWidth: undefined,
16979     /**
16980      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16981      * mode = 'remote' or 'text' if mode = 'local')
16982      */
16983     displayField: undefined,
16984     
16985     /**
16986      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16987      * mode = 'remote' or 'value' if mode = 'local'). 
16988      * Note: use of a valueField requires the user make a selection
16989      * in order for a value to be mapped.
16990      */
16991     valueField: undefined,
16992     /**
16993      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16994      */
16995     modalTitle : '',
16996     
16997     /**
16998      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16999      * field's data value (defaults to the underlying DOM element's name)
17000      */
17001     hiddenName: undefined,
17002     /**
17003      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17004      */
17005     listClass: '',
17006     /**
17007      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17008      */
17009     selectedClass: 'active',
17010     
17011     /**
17012      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17013      */
17014     shadow:'sides',
17015     /**
17016      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17017      * anchor positions (defaults to 'tl-bl')
17018      */
17019     listAlign: 'tl-bl?',
17020     /**
17021      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17022      */
17023     maxHeight: 300,
17024     /**
17025      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17026      * query specified by the allQuery config option (defaults to 'query')
17027      */
17028     triggerAction: 'query',
17029     /**
17030      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17031      * (defaults to 4, does not apply if editable = false)
17032      */
17033     minChars : 4,
17034     /**
17035      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17036      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17037      */
17038     typeAhead: false,
17039     /**
17040      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17041      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17042      */
17043     queryDelay: 500,
17044     /**
17045      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17046      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17047      */
17048     pageSize: 0,
17049     /**
17050      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17051      * when editable = true (defaults to false)
17052      */
17053     selectOnFocus:false,
17054     /**
17055      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17056      */
17057     queryParam: 'query',
17058     /**
17059      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17060      * when mode = 'remote' (defaults to 'Loading...')
17061      */
17062     loadingText: 'Loading...',
17063     /**
17064      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17065      */
17066     resizable: false,
17067     /**
17068      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17069      */
17070     handleHeight : 8,
17071     /**
17072      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17073      * traditional select (defaults to true)
17074      */
17075     editable: true,
17076     /**
17077      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17078      */
17079     allQuery: '',
17080     /**
17081      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17082      */
17083     mode: 'remote',
17084     /**
17085      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17086      * listWidth has a higher value)
17087      */
17088     minListWidth : 70,
17089     /**
17090      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17091      * allow the user to set arbitrary text into the field (defaults to false)
17092      */
17093     forceSelection:false,
17094     /**
17095      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17096      * if typeAhead = true (defaults to 250)
17097      */
17098     typeAheadDelay : 250,
17099     /**
17100      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17101      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17102      */
17103     valueNotFoundText : undefined,
17104     /**
17105      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17106      */
17107     blockFocus : false,
17108     
17109     /**
17110      * @cfg {Boolean} disableClear Disable showing of clear button.
17111      */
17112     disableClear : false,
17113     /**
17114      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17115      */
17116     alwaysQuery : false,
17117     
17118     /**
17119      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17120      */
17121     multiple : false,
17122     
17123     /**
17124      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17125      */
17126     invalidClass : "has-warning",
17127     
17128     /**
17129      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17130      */
17131     validClass : "has-success",
17132     
17133     /**
17134      * @cfg {Boolean} specialFilter (true|false) special filter default false
17135      */
17136     specialFilter : false,
17137     
17138     /**
17139      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17140      */
17141     mobileTouchView : true,
17142     
17143     /**
17144      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17145      */
17146     useNativeIOS : false,
17147     
17148     /**
17149      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17150      */
17151     mobile_restrict_height : false,
17152     
17153     ios_options : false,
17154     
17155     //private
17156     addicon : false,
17157     editicon: false,
17158     
17159     page: 0,
17160     hasQuery: false,
17161     append: false,
17162     loadNext: false,
17163     autoFocus : true,
17164     tickable : false,
17165     btnPosition : 'right',
17166     triggerList : true,
17167     showToggleBtn : true,
17168     animate : true,
17169     emptyResultText: 'Empty',
17170     triggerText : 'Select',
17171     emptyTitle : '',
17172     width : false,
17173     
17174     // element that contains real text value.. (when hidden is used..)
17175     
17176     getAutoCreate : function()
17177     {   
17178         var cfg = false;
17179         //render
17180         /*
17181          * Render classic select for iso
17182          */
17183         
17184         if(Roo.isIOS && this.useNativeIOS){
17185             cfg = this.getAutoCreateNativeIOS();
17186             return cfg;
17187         }
17188         
17189         /*
17190          * Touch Devices
17191          */
17192         
17193         if(Roo.isTouch && this.mobileTouchView){
17194             cfg = this.getAutoCreateTouchView();
17195             return cfg;;
17196         }
17197         
17198         /*
17199          *  Normal ComboBox
17200          */
17201         if(!this.tickable){
17202             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17203             return cfg;
17204         }
17205         
17206         /*
17207          *  ComboBox with tickable selections
17208          */
17209              
17210         var align = this.labelAlign || this.parentLabelAlign();
17211         
17212         cfg = {
17213             cls : 'form-group roo-combobox-tickable' //input-group
17214         };
17215         
17216         var btn_text_select = '';
17217         var btn_text_done = '';
17218         var btn_text_cancel = '';
17219         
17220         if (this.btn_text_show) {
17221             btn_text_select = 'Select';
17222             btn_text_done = 'Done';
17223             btn_text_cancel = 'Cancel'; 
17224         }
17225         
17226         var buttons = {
17227             tag : 'div',
17228             cls : 'tickable-buttons',
17229             cn : [
17230                 {
17231                     tag : 'button',
17232                     type : 'button',
17233                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17234                     //html : this.triggerText
17235                     html: btn_text_select
17236                 },
17237                 {
17238                     tag : 'button',
17239                     type : 'button',
17240                     name : 'ok',
17241                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17242                     //html : 'Done'
17243                     html: btn_text_done
17244                 },
17245                 {
17246                     tag : 'button',
17247                     type : 'button',
17248                     name : 'cancel',
17249                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17250                     //html : 'Cancel'
17251                     html: btn_text_cancel
17252                 }
17253             ]
17254         };
17255         
17256         if(this.editable){
17257             buttons.cn.unshift({
17258                 tag: 'input',
17259                 cls: 'roo-select2-search-field-input'
17260             });
17261         }
17262         
17263         var _this = this;
17264         
17265         Roo.each(buttons.cn, function(c){
17266             if (_this.size) {
17267                 c.cls += ' btn-' + _this.size;
17268             }
17269
17270             if (_this.disabled) {
17271                 c.disabled = true;
17272             }
17273         });
17274         
17275         var box = {
17276             tag: 'div',
17277             style : 'display: contents',
17278             cn: [
17279                 {
17280                     tag: 'input',
17281                     type : 'hidden',
17282                     cls: 'form-hidden-field'
17283                 },
17284                 {
17285                     tag: 'ul',
17286                     cls: 'roo-select2-choices',
17287                     cn:[
17288                         {
17289                             tag: 'li',
17290                             cls: 'roo-select2-search-field',
17291                             cn: [
17292                                 buttons
17293                             ]
17294                         }
17295                     ]
17296                 }
17297             ]
17298         };
17299         
17300         var combobox = {
17301             cls: 'roo-select2-container input-group roo-select2-container-multi',
17302             cn: [
17303                 
17304                 box
17305 //                {
17306 //                    tag: 'ul',
17307 //                    cls: 'typeahead typeahead-long dropdown-menu',
17308 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17309 //                }
17310             ]
17311         };
17312         
17313         if(this.hasFeedback && !this.allowBlank){
17314             
17315             var feedback = {
17316                 tag: 'span',
17317                 cls: 'glyphicon form-control-feedback'
17318             };
17319
17320             combobox.cn.push(feedback);
17321         }
17322         
17323         
17324         
17325         var indicator = {
17326             tag : 'i',
17327             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17328             tooltip : 'This field is required'
17329         };
17330         if (Roo.bootstrap.version == 4) {
17331             indicator = {
17332                 tag : 'i',
17333                 style : 'display:none'
17334             };
17335         }
17336         if (align ==='left' && this.fieldLabel.length) {
17337             
17338             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17339             
17340             cfg.cn = [
17341                 indicator,
17342                 {
17343                     tag: 'label',
17344                     'for' :  id,
17345                     cls : 'control-label col-form-label',
17346                     html : this.fieldLabel
17347
17348                 },
17349                 {
17350                     cls : "", 
17351                     cn: [
17352                         combobox
17353                     ]
17354                 }
17355
17356             ];
17357             
17358             var labelCfg = cfg.cn[1];
17359             var contentCfg = cfg.cn[2];
17360             
17361
17362             if(this.indicatorpos == 'right'){
17363                 
17364                 cfg.cn = [
17365                     {
17366                         tag: 'label',
17367                         'for' :  id,
17368                         cls : 'control-label col-form-label',
17369                         cn : [
17370                             {
17371                                 tag : 'span',
17372                                 html : this.fieldLabel
17373                             },
17374                             indicator
17375                         ]
17376                     },
17377                     {
17378                         cls : "",
17379                         cn: [
17380                             combobox
17381                         ]
17382                     }
17383
17384                 ];
17385                 
17386                 
17387                 
17388                 labelCfg = cfg.cn[0];
17389                 contentCfg = cfg.cn[1];
17390             
17391             }
17392             
17393             if(this.labelWidth > 12){
17394                 labelCfg.style = "width: " + this.labelWidth + 'px';
17395             }
17396             if(this.width * 1 > 0){
17397                 contentCfg.style = "width: " + this.width + 'px';
17398             }
17399             if(this.labelWidth < 13 && this.labelmd == 0){
17400                 this.labelmd = this.labelWidth;
17401             }
17402             
17403             if(this.labellg > 0){
17404                 labelCfg.cls += ' col-lg-' + this.labellg;
17405                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17406             }
17407             
17408             if(this.labelmd > 0){
17409                 labelCfg.cls += ' col-md-' + this.labelmd;
17410                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17411             }
17412             
17413             if(this.labelsm > 0){
17414                 labelCfg.cls += ' col-sm-' + this.labelsm;
17415                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17416             }
17417             
17418             if(this.labelxs > 0){
17419                 labelCfg.cls += ' col-xs-' + this.labelxs;
17420                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17421             }
17422                 
17423                 
17424         } else if ( this.fieldLabel.length) {
17425 //                Roo.log(" label");
17426                  cfg.cn = [
17427                    indicator,
17428                     {
17429                         tag: 'label',
17430                         //cls : 'input-group-addon',
17431                         html : this.fieldLabel
17432                     },
17433                     combobox
17434                 ];
17435                 
17436                 if(this.indicatorpos == 'right'){
17437                     cfg.cn = [
17438                         {
17439                             tag: 'label',
17440                             //cls : 'input-group-addon',
17441                             html : this.fieldLabel
17442                         },
17443                         indicator,
17444                         combobox
17445                     ];
17446                     
17447                 }
17448
17449         } else {
17450             
17451 //                Roo.log(" no label && no align");
17452                 cfg = combobox
17453                      
17454                 
17455         }
17456          
17457         var settings=this;
17458         ['xs','sm','md','lg'].map(function(size){
17459             if (settings[size]) {
17460                 cfg.cls += ' col-' + size + '-' + settings[size];
17461             }
17462         });
17463         
17464         return cfg;
17465         
17466     },
17467     
17468     _initEventsCalled : false,
17469     
17470     // private
17471     initEvents: function()
17472     {   
17473         if (this._initEventsCalled) { // as we call render... prevent looping...
17474             return;
17475         }
17476         this._initEventsCalled = true;
17477         
17478         if (!this.store) {
17479             throw "can not find store for combo";
17480         }
17481         
17482         this.indicator = this.indicatorEl();
17483         
17484         this.store = Roo.factory(this.store, Roo.data);
17485         this.store.parent = this;
17486         
17487         // if we are building from html. then this element is so complex, that we can not really
17488         // use the rendered HTML.
17489         // so we have to trash and replace the previous code.
17490         if (Roo.XComponent.build_from_html) {
17491             // remove this element....
17492             var e = this.el.dom, k=0;
17493             while (e ) { e = e.previousSibling;  ++k;}
17494
17495             this.el.remove();
17496             
17497             this.el=false;
17498             this.rendered = false;
17499             
17500             this.render(this.parent().getChildContainer(true), k);
17501         }
17502         
17503         if(Roo.isIOS && this.useNativeIOS){
17504             this.initIOSView();
17505             return;
17506         }
17507         
17508         /*
17509          * Touch Devices
17510          */
17511         
17512         if(Roo.isTouch && this.mobileTouchView){
17513             this.initTouchView();
17514             return;
17515         }
17516         
17517         if(this.tickable){
17518             this.initTickableEvents();
17519             return;
17520         }
17521         
17522         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17523         
17524         if(this.hiddenName){
17525             
17526             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17527             
17528             this.hiddenField.dom.value =
17529                 this.hiddenValue !== undefined ? this.hiddenValue :
17530                 this.value !== undefined ? this.value : '';
17531
17532             // prevent input submission
17533             this.el.dom.removeAttribute('name');
17534             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17535              
17536              
17537         }
17538         //if(Roo.isGecko){
17539         //    this.el.dom.setAttribute('autocomplete', 'off');
17540         //}
17541         
17542         var cls = 'x-combo-list';
17543         
17544         //this.list = new Roo.Layer({
17545         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17546         //});
17547         
17548         var _this = this;
17549         
17550         (function(){
17551             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17552             _this.list.setWidth(lw);
17553         }).defer(100);
17554         
17555         this.list.on('mouseover', this.onViewOver, this);
17556         this.list.on('mousemove', this.onViewMove, this);
17557         this.list.on('scroll', this.onViewScroll, this);
17558         
17559         /*
17560         this.list.swallowEvent('mousewheel');
17561         this.assetHeight = 0;
17562
17563         if(this.title){
17564             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17565             this.assetHeight += this.header.getHeight();
17566         }
17567
17568         this.innerList = this.list.createChild({cls:cls+'-inner'});
17569         this.innerList.on('mouseover', this.onViewOver, this);
17570         this.innerList.on('mousemove', this.onViewMove, this);
17571         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17572         
17573         if(this.allowBlank && !this.pageSize && !this.disableClear){
17574             this.footer = this.list.createChild({cls:cls+'-ft'});
17575             this.pageTb = new Roo.Toolbar(this.footer);
17576            
17577         }
17578         if(this.pageSize){
17579             this.footer = this.list.createChild({cls:cls+'-ft'});
17580             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17581                     {pageSize: this.pageSize});
17582             
17583         }
17584         
17585         if (this.pageTb && this.allowBlank && !this.disableClear) {
17586             var _this = this;
17587             this.pageTb.add(new Roo.Toolbar.Fill(), {
17588                 cls: 'x-btn-icon x-btn-clear',
17589                 text: '&#160;',
17590                 handler: function()
17591                 {
17592                     _this.collapse();
17593                     _this.clearValue();
17594                     _this.onSelect(false, -1);
17595                 }
17596             });
17597         }
17598         if (this.footer) {
17599             this.assetHeight += this.footer.getHeight();
17600         }
17601         */
17602             
17603         if(!this.tpl){
17604             this.tpl = Roo.bootstrap.version == 4 ?
17605                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17606                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17607         }
17608
17609         this.view = new Roo.View(this.list, this.tpl, {
17610             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17611         });
17612         //this.view.wrapEl.setDisplayed(false);
17613         this.view.on('click', this.onViewClick, this);
17614         
17615         
17616         this.store.on('beforeload', this.onBeforeLoad, this);
17617         this.store.on('load', this.onLoad, this);
17618         this.store.on('loadexception', this.onLoadException, this);
17619         /*
17620         if(this.resizable){
17621             this.resizer = new Roo.Resizable(this.list,  {
17622                pinned:true, handles:'se'
17623             });
17624             this.resizer.on('resize', function(r, w, h){
17625                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17626                 this.listWidth = w;
17627                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17628                 this.restrictHeight();
17629             }, this);
17630             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17631         }
17632         */
17633         if(!this.editable){
17634             this.editable = true;
17635             this.setEditable(false);
17636         }
17637         
17638         /*
17639         
17640         if (typeof(this.events.add.listeners) != 'undefined') {
17641             
17642             this.addicon = this.wrap.createChild(
17643                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17644        
17645             this.addicon.on('click', function(e) {
17646                 this.fireEvent('add', this);
17647             }, this);
17648         }
17649         if (typeof(this.events.edit.listeners) != 'undefined') {
17650             
17651             this.editicon = this.wrap.createChild(
17652                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17653             if (this.addicon) {
17654                 this.editicon.setStyle('margin-left', '40px');
17655             }
17656             this.editicon.on('click', function(e) {
17657                 
17658                 // we fire even  if inothing is selected..
17659                 this.fireEvent('edit', this, this.lastData );
17660                 
17661             }, this);
17662         }
17663         */
17664         
17665         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17666             "up" : function(e){
17667                 this.inKeyMode = true;
17668                 this.selectPrev();
17669             },
17670
17671             "down" : function(e){
17672                 if(!this.isExpanded()){
17673                     this.onTriggerClick();
17674                 }else{
17675                     this.inKeyMode = true;
17676                     this.selectNext();
17677                 }
17678             },
17679
17680             "enter" : function(e){
17681 //                this.onViewClick();
17682                 //return true;
17683                 this.collapse();
17684                 
17685                 if(this.fireEvent("specialkey", this, e)){
17686                     this.onViewClick(false);
17687                 }
17688                 
17689                 return true;
17690             },
17691
17692             "esc" : function(e){
17693                 this.collapse();
17694             },
17695
17696             "tab" : function(e){
17697                 this.collapse();
17698                 
17699                 if(this.fireEvent("specialkey", this, e)){
17700                     this.onViewClick(false);
17701                 }
17702                 
17703                 return true;
17704             },
17705
17706             scope : this,
17707
17708             doRelay : function(foo, bar, hname){
17709                 if(hname == 'down' || this.scope.isExpanded()){
17710                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17711                 }
17712                 return true;
17713             },
17714
17715             forceKeyDown: true
17716         });
17717         
17718         
17719         this.queryDelay = Math.max(this.queryDelay || 10,
17720                 this.mode == 'local' ? 10 : 250);
17721         
17722         
17723         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17724         
17725         if(this.typeAhead){
17726             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17727         }
17728         if(this.editable !== false){
17729             this.inputEl().on("keyup", this.onKeyUp, this);
17730         }
17731         if(this.forceSelection){
17732             this.inputEl().on('blur', this.doForce, this);
17733         }
17734         
17735         if(this.multiple){
17736             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17737             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17738         }
17739     },
17740     
17741     initTickableEvents: function()
17742     {   
17743         this.createList();
17744         
17745         if(this.hiddenName){
17746             
17747             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17748             
17749             this.hiddenField.dom.value =
17750                 this.hiddenValue !== undefined ? this.hiddenValue :
17751                 this.value !== undefined ? this.value : '';
17752
17753             // prevent input submission
17754             this.el.dom.removeAttribute('name');
17755             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17756              
17757              
17758         }
17759         
17760 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17761         
17762         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17763         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17764         if(this.triggerList){
17765             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17766         }
17767          
17768         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17769         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17770         
17771         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17772         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17773         
17774         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17775         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17776         
17777         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17778         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17779         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17780         
17781         this.okBtn.hide();
17782         this.cancelBtn.hide();
17783         
17784         var _this = this;
17785         
17786         (function(){
17787             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17788             _this.list.setWidth(lw);
17789         }).defer(100);
17790         
17791         this.list.on('mouseover', this.onViewOver, this);
17792         this.list.on('mousemove', this.onViewMove, this);
17793         
17794         this.list.on('scroll', this.onViewScroll, this);
17795         
17796         if(!this.tpl){
17797             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17798                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17799         }
17800
17801         this.view = new Roo.View(this.list, this.tpl, {
17802             singleSelect:true,
17803             tickable:true,
17804             parent:this,
17805             store: this.store,
17806             selectedClass: this.selectedClass
17807         });
17808         
17809         //this.view.wrapEl.setDisplayed(false);
17810         this.view.on('click', this.onViewClick, this);
17811         
17812         
17813         
17814         this.store.on('beforeload', this.onBeforeLoad, this);
17815         this.store.on('load', this.onLoad, this);
17816         this.store.on('loadexception', this.onLoadException, this);
17817         
17818         if(this.editable){
17819             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17820                 "up" : function(e){
17821                     this.inKeyMode = true;
17822                     this.selectPrev();
17823                 },
17824
17825                 "down" : function(e){
17826                     this.inKeyMode = true;
17827                     this.selectNext();
17828                 },
17829
17830                 "enter" : function(e){
17831                     if(this.fireEvent("specialkey", this, e)){
17832                         this.onViewClick(false);
17833                     }
17834                     
17835                     return true;
17836                 },
17837
17838                 "esc" : function(e){
17839                     this.onTickableFooterButtonClick(e, false, false);
17840                 },
17841
17842                 "tab" : function(e){
17843                     this.fireEvent("specialkey", this, e);
17844                     
17845                     this.onTickableFooterButtonClick(e, false, false);
17846                     
17847                     return true;
17848                 },
17849
17850                 scope : this,
17851
17852                 doRelay : function(e, fn, key){
17853                     if(this.scope.isExpanded()){
17854                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17855                     }
17856                     return true;
17857                 },
17858
17859                 forceKeyDown: true
17860             });
17861         }
17862         
17863         this.queryDelay = Math.max(this.queryDelay || 10,
17864                 this.mode == 'local' ? 10 : 250);
17865         
17866         
17867         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17868         
17869         if(this.typeAhead){
17870             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17871         }
17872         
17873         if(this.editable !== false){
17874             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17875         }
17876         
17877         this.indicator = this.indicatorEl();
17878         
17879         if(this.indicator){
17880             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17881             this.indicator.hide();
17882         }
17883         
17884     },
17885
17886     onDestroy : function(){
17887         if(this.view){
17888             this.view.setStore(null);
17889             this.view.el.removeAllListeners();
17890             this.view.el.remove();
17891             this.view.purgeListeners();
17892         }
17893         if(this.list){
17894             this.list.dom.innerHTML  = '';
17895         }
17896         
17897         if(this.store){
17898             this.store.un('beforeload', this.onBeforeLoad, this);
17899             this.store.un('load', this.onLoad, this);
17900             this.store.un('loadexception', this.onLoadException, this);
17901         }
17902         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17903     },
17904
17905     // private
17906     fireKey : function(e){
17907         if(e.isNavKeyPress() && !this.list.isVisible()){
17908             this.fireEvent("specialkey", this, e);
17909         }
17910     },
17911
17912     // private
17913     onResize: function(w, h)
17914     {
17915         
17916         
17917 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17918 //        
17919 //        if(typeof w != 'number'){
17920 //            // we do not handle it!?!?
17921 //            return;
17922 //        }
17923 //        var tw = this.trigger.getWidth();
17924 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17925 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17926 //        var x = w - tw;
17927 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17928 //            
17929 //        //this.trigger.setStyle('left', x+'px');
17930 //        
17931 //        if(this.list && this.listWidth === undefined){
17932 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17933 //            this.list.setWidth(lw);
17934 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17935 //        }
17936         
17937     
17938         
17939     },
17940
17941     /**
17942      * Allow or prevent the user from directly editing the field text.  If false is passed,
17943      * the user will only be able to select from the items defined in the dropdown list.  This method
17944      * is the runtime equivalent of setting the 'editable' config option at config time.
17945      * @param {Boolean} value True to allow the user to directly edit the field text
17946      */
17947     setEditable : function(value){
17948         if(value == this.editable){
17949             return;
17950         }
17951         this.editable = value;
17952         if(!value){
17953             this.inputEl().dom.setAttribute('readOnly', true);
17954             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17955             this.inputEl().addClass('x-combo-noedit');
17956         }else{
17957             this.inputEl().dom.removeAttribute('readOnly');
17958             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17959             this.inputEl().removeClass('x-combo-noedit');
17960         }
17961     },
17962
17963     // private
17964     
17965     onBeforeLoad : function(combo,opts){
17966         if(!this.hasFocus){
17967             return;
17968         }
17969          if (!opts.add) {
17970             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17971          }
17972         this.restrictHeight();
17973         this.selectedIndex = -1;
17974     },
17975
17976     // private
17977     onLoad : function(){
17978         
17979         this.hasQuery = false;
17980         
17981         if(!this.hasFocus){
17982             return;
17983         }
17984         
17985         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17986             this.loading.hide();
17987         }
17988         
17989         if(this.store.getCount() > 0){
17990             
17991             this.expand();
17992             this.restrictHeight();
17993             if(this.lastQuery == this.allQuery){
17994                 if(this.editable && !this.tickable){
17995                     this.inputEl().dom.select();
17996                 }
17997                 
17998                 if(
17999                     !this.selectByValue(this.value, true) &&
18000                     this.autoFocus && 
18001                     (
18002                         !this.store.lastOptions ||
18003                         typeof(this.store.lastOptions.add) == 'undefined' || 
18004                         this.store.lastOptions.add != true
18005                     )
18006                 ){
18007                     this.select(0, true);
18008                 }
18009             }else{
18010                 if(this.autoFocus){
18011                     this.selectNext();
18012                 }
18013                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18014                     this.taTask.delay(this.typeAheadDelay);
18015                 }
18016             }
18017         }else{
18018             this.onEmptyResults();
18019         }
18020         
18021         //this.el.focus();
18022     },
18023     // private
18024     onLoadException : function()
18025     {
18026         this.hasQuery = false;
18027         
18028         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18029             this.loading.hide();
18030         }
18031         
18032         if(this.tickable && this.editable){
18033             return;
18034         }
18035         
18036         this.collapse();
18037         // only causes errors at present
18038         //Roo.log(this.store.reader.jsonData);
18039         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18040             // fixme
18041             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18042         //}
18043         
18044         
18045     },
18046     // private
18047     onTypeAhead : function(){
18048         if(this.store.getCount() > 0){
18049             var r = this.store.getAt(0);
18050             var newValue = r.data[this.displayField];
18051             var len = newValue.length;
18052             var selStart = this.getRawValue().length;
18053             
18054             if(selStart != len){
18055                 this.setRawValue(newValue);
18056                 this.selectText(selStart, newValue.length);
18057             }
18058         }
18059     },
18060
18061     // private
18062     onSelect : function(record, index){
18063         
18064         if(this.fireEvent('beforeselect', this, record, index) !== false){
18065         
18066             this.setFromData(index > -1 ? record.data : false);
18067             
18068             this.collapse();
18069             this.fireEvent('select', this, record, index);
18070         }
18071     },
18072
18073     /**
18074      * Returns the currently selected field value or empty string if no value is set.
18075      * @return {String} value The selected value
18076      */
18077     getValue : function()
18078     {
18079         if(Roo.isIOS && this.useNativeIOS){
18080             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18081         }
18082         
18083         if(this.multiple){
18084             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18085         }
18086         
18087         if(this.valueField){
18088             return typeof this.value != 'undefined' ? this.value : '';
18089         }else{
18090             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18091         }
18092     },
18093     
18094     getRawValue : function()
18095     {
18096         if(Roo.isIOS && this.useNativeIOS){
18097             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18098         }
18099         
18100         var v = this.inputEl().getValue();
18101         
18102         return v;
18103     },
18104
18105     /**
18106      * Clears any text/value currently set in the field
18107      */
18108     clearValue : function(){
18109         
18110         if(this.hiddenField){
18111             this.hiddenField.dom.value = '';
18112         }
18113         this.value = '';
18114         this.setRawValue('');
18115         this.lastSelectionText = '';
18116         this.lastData = false;
18117         
18118         var close = this.closeTriggerEl();
18119         
18120         if(close){
18121             close.hide();
18122         }
18123         
18124         this.validate();
18125         
18126     },
18127
18128     /**
18129      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18130      * will be displayed in the field.  If the value does not match the data value of an existing item,
18131      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18132      * Otherwise the field will be blank (although the value will still be set).
18133      * @param {String} value The value to match
18134      */
18135     setValue : function(v)
18136     {
18137         if(Roo.isIOS && this.useNativeIOS){
18138             this.setIOSValue(v);
18139             return;
18140         }
18141         
18142         if(this.multiple){
18143             this.syncValue();
18144             return;
18145         }
18146         
18147         var text = v;
18148         if(this.valueField){
18149             var r = this.findRecord(this.valueField, v);
18150             if(r){
18151                 text = r.data[this.displayField];
18152             }else if(this.valueNotFoundText !== undefined){
18153                 text = this.valueNotFoundText;
18154             }
18155         }
18156         this.lastSelectionText = text;
18157         if(this.hiddenField){
18158             this.hiddenField.dom.value = v;
18159         }
18160         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18161         this.value = v;
18162         
18163         var close = this.closeTriggerEl();
18164         
18165         if(close){
18166             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18167         }
18168         
18169         this.validate();
18170     },
18171     /**
18172      * @property {Object} the last set data for the element
18173      */
18174     
18175     lastData : false,
18176     /**
18177      * Sets the value of the field based on a object which is related to the record format for the store.
18178      * @param {Object} value the value to set as. or false on reset?
18179      */
18180     setFromData : function(o){
18181         
18182         if(this.multiple){
18183             this.addItem(o);
18184             return;
18185         }
18186             
18187         var dv = ''; // display value
18188         var vv = ''; // value value..
18189         this.lastData = o;
18190         if (this.displayField) {
18191             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18192         } else {
18193             // this is an error condition!!!
18194             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18195         }
18196         
18197         if(this.valueField){
18198             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18199         }
18200         
18201         var close = this.closeTriggerEl();
18202         
18203         if(close){
18204             if(dv.length || vv * 1 > 0){
18205                 close.show() ;
18206                 this.blockFocus=true;
18207             } else {
18208                 close.hide();
18209             }             
18210         }
18211         
18212         if(this.hiddenField){
18213             this.hiddenField.dom.value = vv;
18214             
18215             this.lastSelectionText = dv;
18216             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18217             this.value = vv;
18218             return;
18219         }
18220         // no hidden field.. - we store the value in 'value', but still display
18221         // display field!!!!
18222         this.lastSelectionText = dv;
18223         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18224         this.value = vv;
18225         
18226         
18227         
18228     },
18229     // private
18230     reset : function(){
18231         // overridden so that last data is reset..
18232         
18233         if(this.multiple){
18234             this.clearItem();
18235             return;
18236         }
18237         
18238         this.setValue(this.originalValue);
18239         //this.clearInvalid();
18240         this.lastData = false;
18241         if (this.view) {
18242             this.view.clearSelections();
18243         }
18244         
18245         this.validate();
18246     },
18247     // private
18248     findRecord : function(prop, value){
18249         var record;
18250         if(this.store.getCount() > 0){
18251             this.store.each(function(r){
18252                 if(r.data[prop] == value){
18253                     record = r;
18254                     return false;
18255                 }
18256                 return true;
18257             });
18258         }
18259         return record;
18260     },
18261     
18262     getName: function()
18263     {
18264         // returns hidden if it's set..
18265         if (!this.rendered) {return ''};
18266         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18267         
18268     },
18269     // private
18270     onViewMove : function(e, t){
18271         this.inKeyMode = false;
18272     },
18273
18274     // private
18275     onViewOver : function(e, t){
18276         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18277             return;
18278         }
18279         var item = this.view.findItemFromChild(t);
18280         
18281         if(item){
18282             var index = this.view.indexOf(item);
18283             this.select(index, false);
18284         }
18285     },
18286
18287     // private
18288     onViewClick : function(view, doFocus, el, e)
18289     {
18290         var index = this.view.getSelectedIndexes()[0];
18291         
18292         var r = this.store.getAt(index);
18293         
18294         if(this.tickable){
18295             
18296             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18297                 return;
18298             }
18299             
18300             var rm = false;
18301             var _this = this;
18302             
18303             Roo.each(this.tickItems, function(v,k){
18304                 
18305                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18306                     Roo.log(v);
18307                     _this.tickItems.splice(k, 1);
18308                     
18309                     if(typeof(e) == 'undefined' && view == false){
18310                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18311                     }
18312                     
18313                     rm = true;
18314                     return;
18315                 }
18316             });
18317             
18318             if(rm){
18319                 return;
18320             }
18321             
18322             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18323                 this.tickItems.push(r.data);
18324             }
18325             
18326             if(typeof(e) == 'undefined' && view == false){
18327                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18328             }
18329                     
18330             return;
18331         }
18332         
18333         if(r){
18334             this.onSelect(r, index);
18335         }
18336         if(doFocus !== false && !this.blockFocus){
18337             this.inputEl().focus();
18338         }
18339     },
18340
18341     // private
18342     restrictHeight : function(){
18343         //this.innerList.dom.style.height = '';
18344         //var inner = this.innerList.dom;
18345         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18346         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18347         //this.list.beginUpdate();
18348         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18349         this.list.alignTo(this.inputEl(), this.listAlign);
18350         this.list.alignTo(this.inputEl(), this.listAlign);
18351         //this.list.endUpdate();
18352     },
18353
18354     // private
18355     onEmptyResults : function(){
18356         
18357         if(this.tickable && this.editable){
18358             this.hasFocus = false;
18359             this.restrictHeight();
18360             return;
18361         }
18362         
18363         this.collapse();
18364     },
18365
18366     /**
18367      * Returns true if the dropdown list is expanded, else false.
18368      */
18369     isExpanded : function(){
18370         return this.list.isVisible();
18371     },
18372
18373     /**
18374      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18375      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18376      * @param {String} value The data value of the item to select
18377      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18378      * selected item if it is not currently in view (defaults to true)
18379      * @return {Boolean} True if the value matched an item in the list, else false
18380      */
18381     selectByValue : function(v, scrollIntoView){
18382         if(v !== undefined && v !== null){
18383             var r = this.findRecord(this.valueField || this.displayField, v);
18384             if(r){
18385                 this.select(this.store.indexOf(r), scrollIntoView);
18386                 return true;
18387             }
18388         }
18389         return false;
18390     },
18391
18392     /**
18393      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18394      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18395      * @param {Number} index The zero-based index of the list item to select
18396      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18397      * selected item if it is not currently in view (defaults to true)
18398      */
18399     select : function(index, scrollIntoView){
18400         this.selectedIndex = index;
18401         this.view.select(index);
18402         if(scrollIntoView !== false){
18403             var el = this.view.getNode(index);
18404             /*
18405              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18406              */
18407             if(el){
18408                 this.list.scrollChildIntoView(el, false);
18409             }
18410         }
18411     },
18412
18413     // private
18414     selectNext : function(){
18415         var ct = this.store.getCount();
18416         if(ct > 0){
18417             if(this.selectedIndex == -1){
18418                 this.select(0);
18419             }else if(this.selectedIndex < ct-1){
18420                 this.select(this.selectedIndex+1);
18421             }
18422         }
18423     },
18424
18425     // private
18426     selectPrev : function(){
18427         var ct = this.store.getCount();
18428         if(ct > 0){
18429             if(this.selectedIndex == -1){
18430                 this.select(0);
18431             }else if(this.selectedIndex != 0){
18432                 this.select(this.selectedIndex-1);
18433             }
18434         }
18435     },
18436
18437     // private
18438     onKeyUp : function(e){
18439         if(this.editable !== false && !e.isSpecialKey()){
18440             this.lastKey = e.getKey();
18441             this.dqTask.delay(this.queryDelay);
18442         }
18443     },
18444
18445     // private
18446     validateBlur : function(){
18447         return !this.list || !this.list.isVisible();   
18448     },
18449
18450     // private
18451     initQuery : function(){
18452         
18453         var v = this.getRawValue();
18454         
18455         if(this.tickable && this.editable){
18456             v = this.tickableInputEl().getValue();
18457         }
18458         
18459         this.doQuery(v);
18460     },
18461
18462     // private
18463     doForce : function(){
18464         if(this.inputEl().dom.value.length > 0){
18465             this.inputEl().dom.value =
18466                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18467              
18468         }
18469     },
18470
18471     /**
18472      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18473      * query allowing the query action to be canceled if needed.
18474      * @param {String} query The SQL query to execute
18475      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18476      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18477      * saved in the current store (defaults to false)
18478      */
18479     doQuery : function(q, forceAll){
18480         
18481         if(q === undefined || q === null){
18482             q = '';
18483         }
18484         var qe = {
18485             query: q,
18486             forceAll: forceAll,
18487             combo: this,
18488             cancel:false
18489         };
18490         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18491             return false;
18492         }
18493         q = qe.query;
18494         
18495         forceAll = qe.forceAll;
18496         if(forceAll === true || (q.length >= this.minChars)){
18497             
18498             this.hasQuery = true;
18499             
18500             if(this.lastQuery != q || this.alwaysQuery){
18501                 this.lastQuery = q;
18502                 if(this.mode == 'local'){
18503                     this.selectedIndex = -1;
18504                     if(forceAll){
18505                         this.store.clearFilter();
18506                     }else{
18507                         
18508                         if(this.specialFilter){
18509                             this.fireEvent('specialfilter', this);
18510                             this.onLoad();
18511                             return;
18512                         }
18513                         
18514                         this.store.filter(this.displayField, q);
18515                     }
18516                     
18517                     this.store.fireEvent("datachanged", this.store);
18518                     
18519                     this.onLoad();
18520                     
18521                     
18522                 }else{
18523                     
18524                     this.store.baseParams[this.queryParam] = q;
18525                     
18526                     var options = {params : this.getParams(q)};
18527                     
18528                     if(this.loadNext){
18529                         options.add = true;
18530                         options.params.start = this.page * this.pageSize;
18531                     }
18532                     
18533                     this.store.load(options);
18534                     
18535                     /*
18536                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18537                      *  we should expand the list on onLoad
18538                      *  so command out it
18539                      */
18540 //                    this.expand();
18541                 }
18542             }else{
18543                 this.selectedIndex = -1;
18544                 this.onLoad();   
18545             }
18546         }
18547         
18548         this.loadNext = false;
18549     },
18550     
18551     // private
18552     getParams : function(q){
18553         var p = {};
18554         //p[this.queryParam] = q;
18555         
18556         if(this.pageSize){
18557             p.start = 0;
18558             p.limit = this.pageSize;
18559         }
18560         return p;
18561     },
18562
18563     /**
18564      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18565      */
18566     collapse : function(){
18567         if(!this.isExpanded()){
18568             return;
18569         }
18570         
18571         this.list.hide();
18572         
18573         this.hasFocus = false;
18574         
18575         if(this.tickable){
18576             this.okBtn.hide();
18577             this.cancelBtn.hide();
18578             this.trigger.show();
18579             
18580             if(this.editable){
18581                 this.tickableInputEl().dom.value = '';
18582                 this.tickableInputEl().blur();
18583             }
18584             
18585         }
18586         
18587         Roo.get(document).un('mousedown', this.collapseIf, this);
18588         Roo.get(document).un('mousewheel', this.collapseIf, this);
18589         if (!this.editable) {
18590             Roo.get(document).un('keydown', this.listKeyPress, this);
18591         }
18592         this.fireEvent('collapse', this);
18593         
18594         this.validate();
18595     },
18596
18597     // private
18598     collapseIf : function(e){
18599         var in_combo  = e.within(this.el);
18600         var in_list =  e.within(this.list);
18601         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18602         
18603         if (in_combo || in_list || is_list) {
18604             //e.stopPropagation();
18605             return;
18606         }
18607         
18608         if(this.tickable){
18609             this.onTickableFooterButtonClick(e, false, false);
18610         }
18611
18612         this.collapse();
18613         
18614     },
18615
18616     /**
18617      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18618      */
18619     expand : function(){
18620        
18621         if(this.isExpanded() || !this.hasFocus){
18622             return;
18623         }
18624         
18625         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18626         this.list.setWidth(lw);
18627         
18628         Roo.log('expand');
18629         
18630         this.list.show();
18631         
18632         this.restrictHeight();
18633         
18634         if(this.tickable){
18635             
18636             this.tickItems = Roo.apply([], this.item);
18637             
18638             this.okBtn.show();
18639             this.cancelBtn.show();
18640             this.trigger.hide();
18641             
18642             if(this.editable){
18643                 this.tickableInputEl().focus();
18644             }
18645             
18646         }
18647         
18648         Roo.get(document).on('mousedown', this.collapseIf, this);
18649         Roo.get(document).on('mousewheel', this.collapseIf, this);
18650         if (!this.editable) {
18651             Roo.get(document).on('keydown', this.listKeyPress, this);
18652         }
18653         
18654         this.fireEvent('expand', this);
18655     },
18656
18657     // private
18658     // Implements the default empty TriggerField.onTriggerClick function
18659     onTriggerClick : function(e)
18660     {
18661         Roo.log('trigger click');
18662         
18663         if(this.disabled || !this.triggerList){
18664             return;
18665         }
18666         
18667         this.page = 0;
18668         this.loadNext = false;
18669         
18670         if(this.isExpanded()){
18671             this.collapse();
18672             if (!this.blockFocus) {
18673                 this.inputEl().focus();
18674             }
18675             
18676         }else {
18677             this.hasFocus = true;
18678             if(this.triggerAction == 'all') {
18679                 this.doQuery(this.allQuery, true);
18680             } else {
18681                 this.doQuery(this.getRawValue());
18682             }
18683             if (!this.blockFocus) {
18684                 this.inputEl().focus();
18685             }
18686         }
18687     },
18688     
18689     onTickableTriggerClick : function(e)
18690     {
18691         if(this.disabled){
18692             return;
18693         }
18694         
18695         this.page = 0;
18696         this.loadNext = false;
18697         this.hasFocus = true;
18698         
18699         if(this.triggerAction == 'all') {
18700             this.doQuery(this.allQuery, true);
18701         } else {
18702             this.doQuery(this.getRawValue());
18703         }
18704     },
18705     
18706     onSearchFieldClick : function(e)
18707     {
18708         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18709             this.onTickableFooterButtonClick(e, false, false);
18710             return;
18711         }
18712         
18713         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18714             return;
18715         }
18716         
18717         this.page = 0;
18718         this.loadNext = false;
18719         this.hasFocus = true;
18720         
18721         if(this.triggerAction == 'all') {
18722             this.doQuery(this.allQuery, true);
18723         } else {
18724             this.doQuery(this.getRawValue());
18725         }
18726     },
18727     
18728     listKeyPress : function(e)
18729     {
18730         //Roo.log('listkeypress');
18731         // scroll to first matching element based on key pres..
18732         if (e.isSpecialKey()) {
18733             return false;
18734         }
18735         var k = String.fromCharCode(e.getKey()).toUpperCase();
18736         //Roo.log(k);
18737         var match  = false;
18738         var csel = this.view.getSelectedNodes();
18739         var cselitem = false;
18740         if (csel.length) {
18741             var ix = this.view.indexOf(csel[0]);
18742             cselitem  = this.store.getAt(ix);
18743             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18744                 cselitem = false;
18745             }
18746             
18747         }
18748         
18749         this.store.each(function(v) { 
18750             if (cselitem) {
18751                 // start at existing selection.
18752                 if (cselitem.id == v.id) {
18753                     cselitem = false;
18754                 }
18755                 return true;
18756             }
18757                 
18758             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18759                 match = this.store.indexOf(v);
18760                 return false;
18761             }
18762             return true;
18763         }, this);
18764         
18765         if (match === false) {
18766             return true; // no more action?
18767         }
18768         // scroll to?
18769         this.view.select(match);
18770         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18771         sn.scrollIntoView(sn.dom.parentNode, false);
18772     },
18773     
18774     onViewScroll : function(e, t){
18775         
18776         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){
18777             return;
18778         }
18779         
18780         this.hasQuery = true;
18781         
18782         this.loading = this.list.select('.loading', true).first();
18783         
18784         if(this.loading === null){
18785             this.list.createChild({
18786                 tag: 'div',
18787                 cls: 'loading roo-select2-more-results roo-select2-active',
18788                 html: 'Loading more results...'
18789             });
18790             
18791             this.loading = this.list.select('.loading', true).first();
18792             
18793             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18794             
18795             this.loading.hide();
18796         }
18797         
18798         this.loading.show();
18799         
18800         var _combo = this;
18801         
18802         this.page++;
18803         this.loadNext = true;
18804         
18805         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18806         
18807         return;
18808     },
18809     
18810     addItem : function(o)
18811     {   
18812         var dv = ''; // display value
18813         
18814         if (this.displayField) {
18815             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18816         } else {
18817             // this is an error condition!!!
18818             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18819         }
18820         
18821         if(!dv.length){
18822             return;
18823         }
18824         
18825         var choice = this.choices.createChild({
18826             tag: 'li',
18827             cls: 'roo-select2-search-choice',
18828             cn: [
18829                 {
18830                     tag: 'div',
18831                     html: dv
18832                 },
18833                 {
18834                     tag: 'a',
18835                     href: '#',
18836                     cls: 'roo-select2-search-choice-close fa fa-times',
18837                     tabindex: '-1'
18838                 }
18839             ]
18840             
18841         }, this.searchField);
18842         
18843         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18844         
18845         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18846         
18847         this.item.push(o);
18848         
18849         this.lastData = o;
18850         
18851         this.syncValue();
18852         
18853         this.inputEl().dom.value = '';
18854         
18855         this.validate();
18856     },
18857     
18858     onRemoveItem : function(e, _self, o)
18859     {
18860         e.preventDefault();
18861         
18862         this.lastItem = Roo.apply([], this.item);
18863         
18864         var index = this.item.indexOf(o.data) * 1;
18865         
18866         if( index < 0){
18867             Roo.log('not this item?!');
18868             return;
18869         }
18870         
18871         this.item.splice(index, 1);
18872         o.item.remove();
18873         
18874         this.syncValue();
18875         
18876         this.fireEvent('remove', this, e);
18877         
18878         this.validate();
18879         
18880     },
18881     
18882     syncValue : function()
18883     {
18884         if(!this.item.length){
18885             this.clearValue();
18886             return;
18887         }
18888             
18889         var value = [];
18890         var _this = this;
18891         Roo.each(this.item, function(i){
18892             if(_this.valueField){
18893                 value.push(i[_this.valueField]);
18894                 return;
18895             }
18896
18897             value.push(i);
18898         });
18899
18900         this.value = value.join(',');
18901
18902         if(this.hiddenField){
18903             this.hiddenField.dom.value = this.value;
18904         }
18905         
18906         this.store.fireEvent("datachanged", this.store);
18907         
18908         this.validate();
18909     },
18910     
18911     clearItem : function()
18912     {
18913         if(!this.multiple){
18914             return;
18915         }
18916         
18917         this.item = [];
18918         
18919         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18920            c.remove();
18921         });
18922         
18923         this.syncValue();
18924         
18925         this.validate();
18926         
18927         if(this.tickable && !Roo.isTouch){
18928             this.view.refresh();
18929         }
18930     },
18931     
18932     inputEl: function ()
18933     {
18934         if(Roo.isIOS && this.useNativeIOS){
18935             return this.el.select('select.roo-ios-select', true).first();
18936         }
18937         
18938         if(Roo.isTouch && this.mobileTouchView){
18939             return this.el.select('input.form-control',true).first();
18940         }
18941         
18942         if(this.tickable){
18943             return this.searchField;
18944         }
18945         
18946         return this.el.select('input.form-control',true).first();
18947     },
18948     
18949     onTickableFooterButtonClick : function(e, btn, el)
18950     {
18951         e.preventDefault();
18952         
18953         this.lastItem = Roo.apply([], this.item);
18954         
18955         if(btn && btn.name == 'cancel'){
18956             this.tickItems = Roo.apply([], this.item);
18957             this.collapse();
18958             return;
18959         }
18960         
18961         this.clearItem();
18962         
18963         var _this = this;
18964         
18965         Roo.each(this.tickItems, function(o){
18966             _this.addItem(o);
18967         });
18968         
18969         this.collapse();
18970         
18971     },
18972     
18973     validate : function()
18974     {
18975         if(this.getVisibilityEl().hasClass('hidden')){
18976             return true;
18977         }
18978         
18979         var v = this.getRawValue();
18980         
18981         if(this.multiple){
18982             v = this.getValue();
18983         }
18984         
18985         if(this.disabled || this.allowBlank || v.length){
18986             this.markValid();
18987             return true;
18988         }
18989         
18990         this.markInvalid();
18991         return false;
18992     },
18993     
18994     tickableInputEl : function()
18995     {
18996         if(!this.tickable || !this.editable){
18997             return this.inputEl();
18998         }
18999         
19000         return this.inputEl().select('.roo-select2-search-field-input', true).first();
19001     },
19002     
19003     
19004     getAutoCreateTouchView : function()
19005     {
19006         var id = Roo.id();
19007         
19008         var cfg = {
19009             cls: 'form-group' //input-group
19010         };
19011         
19012         var input =  {
19013             tag: 'input',
19014             id : id,
19015             type : this.inputType,
19016             cls : 'form-control x-combo-noedit',
19017             autocomplete: 'new-password',
19018             placeholder : this.placeholder || '',
19019             readonly : true
19020         };
19021         
19022         if (this.name) {
19023             input.name = this.name;
19024         }
19025         
19026         if (this.size) {
19027             input.cls += ' input-' + this.size;
19028         }
19029         
19030         if (this.disabled) {
19031             input.disabled = true;
19032         }
19033         
19034         var inputblock = {
19035             cls : 'roo-combobox-wrap',
19036             cn : [
19037                 input
19038             ]
19039         };
19040         
19041         if(this.before){
19042             inputblock.cls += ' input-group';
19043             
19044             inputblock.cn.unshift({
19045                 tag :'span',
19046                 cls : 'input-group-addon input-group-prepend input-group-text',
19047                 html : this.before
19048             });
19049         }
19050         
19051         if(this.removable && !this.multiple){
19052             inputblock.cls += ' roo-removable';
19053             
19054             inputblock.cn.push({
19055                 tag: 'button',
19056                 html : 'x',
19057                 cls : 'roo-combo-removable-btn close'
19058             });
19059         }
19060
19061         if(this.hasFeedback && !this.allowBlank){
19062             
19063             inputblock.cls += ' has-feedback';
19064             
19065             inputblock.cn.push({
19066                 tag: 'span',
19067                 cls: 'glyphicon form-control-feedback'
19068             });
19069             
19070         }
19071         
19072         if (this.after) {
19073             
19074             inputblock.cls += (this.before) ? '' : ' input-group';
19075             
19076             inputblock.cn.push({
19077                 tag :'span',
19078                 cls : 'input-group-addon input-group-append input-group-text',
19079                 html : this.after
19080             });
19081         }
19082
19083         
19084         var ibwrap = inputblock;
19085         
19086         if(this.multiple){
19087             ibwrap = {
19088                 tag: 'ul',
19089                 cls: 'roo-select2-choices',
19090                 cn:[
19091                     {
19092                         tag: 'li',
19093                         cls: 'roo-select2-search-field',
19094                         cn: [
19095
19096                             inputblock
19097                         ]
19098                     }
19099                 ]
19100             };
19101         
19102             
19103         }
19104         
19105         var combobox = {
19106             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19107             cn: [
19108                 {
19109                     tag: 'input',
19110                     type : 'hidden',
19111                     cls: 'form-hidden-field'
19112                 },
19113                 ibwrap
19114             ]
19115         };
19116         
19117         if(!this.multiple && this.showToggleBtn){
19118             
19119             var caret = {
19120                 cls: 'caret'
19121             };
19122             
19123             if (this.caret != false) {
19124                 caret = {
19125                      tag: 'i',
19126                      cls: 'fa fa-' + this.caret
19127                 };
19128                 
19129             }
19130             
19131             combobox.cn.push({
19132                 tag :'span',
19133                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19134                 cn : [
19135                     Roo.bootstrap.version == 3 ? caret : '',
19136                     {
19137                         tag: 'span',
19138                         cls: 'combobox-clear',
19139                         cn  : [
19140                             {
19141                                 tag : 'i',
19142                                 cls: 'icon-remove'
19143                             }
19144                         ]
19145                     }
19146                 ]
19147
19148             })
19149         }
19150         
19151         if(this.multiple){
19152             combobox.cls += ' roo-select2-container-multi';
19153         }
19154         
19155         var required =  this.allowBlank ?  {
19156                     tag : 'i',
19157                     style: 'display: none'
19158                 } : {
19159                    tag : 'i',
19160                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19161                    tooltip : 'This field is required'
19162                 };
19163         
19164         var align = this.labelAlign || this.parentLabelAlign();
19165         
19166         if (align ==='left' && this.fieldLabel.length) {
19167
19168             cfg.cn = [
19169                 required,
19170                 {
19171                     tag: 'label',
19172                     cls : 'control-label col-form-label',
19173                     html : this.fieldLabel
19174
19175                 },
19176                 {
19177                     cls : 'roo-combobox-wrap ', 
19178                     cn: [
19179                         combobox
19180                     ]
19181                 }
19182             ];
19183             
19184             var labelCfg = cfg.cn[1];
19185             var contentCfg = cfg.cn[2];
19186             
19187
19188             if(this.indicatorpos == 'right'){
19189                 cfg.cn = [
19190                     {
19191                         tag: 'label',
19192                         'for' :  id,
19193                         cls : 'control-label col-form-label',
19194                         cn : [
19195                             {
19196                                 tag : 'span',
19197                                 html : this.fieldLabel
19198                             },
19199                             required
19200                         ]
19201                     },
19202                     {
19203                         cls : "roo-combobox-wrap ",
19204                         cn: [
19205                             combobox
19206                         ]
19207                     }
19208
19209                 ];
19210                 
19211                 labelCfg = cfg.cn[0];
19212                 contentCfg = cfg.cn[1];
19213             }
19214             
19215            
19216             
19217             if(this.labelWidth > 12){
19218                 labelCfg.style = "width: " + this.labelWidth + 'px';
19219             }
19220            
19221             if(this.labelWidth < 13 && this.labelmd == 0){
19222                 this.labelmd = this.labelWidth;
19223             }
19224             
19225             if(this.labellg > 0){
19226                 labelCfg.cls += ' col-lg-' + this.labellg;
19227                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19228             }
19229             
19230             if(this.labelmd > 0){
19231                 labelCfg.cls += ' col-md-' + this.labelmd;
19232                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19233             }
19234             
19235             if(this.labelsm > 0){
19236                 labelCfg.cls += ' col-sm-' + this.labelsm;
19237                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19238             }
19239             
19240             if(this.labelxs > 0){
19241                 labelCfg.cls += ' col-xs-' + this.labelxs;
19242                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19243             }
19244                 
19245                 
19246         } else if ( this.fieldLabel.length) {
19247             cfg.cn = [
19248                required,
19249                 {
19250                     tag: 'label',
19251                     cls : 'control-label',
19252                     html : this.fieldLabel
19253
19254                 },
19255                 {
19256                     cls : '', 
19257                     cn: [
19258                         combobox
19259                     ]
19260                 }
19261             ];
19262             
19263             if(this.indicatorpos == 'right'){
19264                 cfg.cn = [
19265                     {
19266                         tag: 'label',
19267                         cls : 'control-label',
19268                         html : this.fieldLabel,
19269                         cn : [
19270                             required
19271                         ]
19272                     },
19273                     {
19274                         cls : '', 
19275                         cn: [
19276                             combobox
19277                         ]
19278                     }
19279                 ];
19280             }
19281         } else {
19282             cfg.cn = combobox;    
19283         }
19284         
19285         
19286         var settings = this;
19287         
19288         ['xs','sm','md','lg'].map(function(size){
19289             if (settings[size]) {
19290                 cfg.cls += ' col-' + size + '-' + settings[size];
19291             }
19292         });
19293         
19294         return cfg;
19295     },
19296     
19297     initTouchView : function()
19298     {
19299         this.renderTouchView();
19300         
19301         this.touchViewEl.on('scroll', function(){
19302             this.el.dom.scrollTop = 0;
19303         }, this);
19304         
19305         this.originalValue = this.getValue();
19306         
19307         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19308         
19309         this.inputEl().on("click", this.showTouchView, this);
19310         if (this.triggerEl) {
19311             this.triggerEl.on("click", this.showTouchView, this);
19312         }
19313         
19314         
19315         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19316         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19317         
19318         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19319         
19320         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19321         this.store.on('load', this.onTouchViewLoad, this);
19322         this.store.on('loadexception', this.onTouchViewLoadException, this);
19323         
19324         if(this.hiddenName){
19325             
19326             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19327             
19328             this.hiddenField.dom.value =
19329                 this.hiddenValue !== undefined ? this.hiddenValue :
19330                 this.value !== undefined ? this.value : '';
19331         
19332             this.el.dom.removeAttribute('name');
19333             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19334         }
19335         
19336         if(this.multiple){
19337             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19338             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19339         }
19340         
19341         if(this.removable && !this.multiple){
19342             var close = this.closeTriggerEl();
19343             if(close){
19344                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19345                 close.on('click', this.removeBtnClick, this, close);
19346             }
19347         }
19348         /*
19349          * fix the bug in Safari iOS8
19350          */
19351         this.inputEl().on("focus", function(e){
19352             document.activeElement.blur();
19353         }, this);
19354         
19355         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19356         
19357         return;
19358         
19359         
19360     },
19361     
19362     renderTouchView : function()
19363     {
19364         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19365         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19366         
19367         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19368         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19369         
19370         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19371         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19372         this.touchViewBodyEl.setStyle('overflow', 'auto');
19373         
19374         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19375         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19376         
19377         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19378         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19379         
19380     },
19381     
19382     showTouchView : function()
19383     {
19384         if(this.disabled){
19385             return;
19386         }
19387         
19388         this.touchViewHeaderEl.hide();
19389
19390         if(this.modalTitle.length){
19391             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19392             this.touchViewHeaderEl.show();
19393         }
19394
19395         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19396         this.touchViewEl.show();
19397
19398         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19399         
19400         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19401         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19402
19403         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19404
19405         if(this.modalTitle.length){
19406             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19407         }
19408         
19409         this.touchViewBodyEl.setHeight(bodyHeight);
19410
19411         if(this.animate){
19412             var _this = this;
19413             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19414         }else{
19415             this.touchViewEl.addClass(['in','show']);
19416         }
19417         
19418         if(this._touchViewMask){
19419             Roo.get(document.body).addClass("x-body-masked");
19420             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19421             this._touchViewMask.setStyle('z-index', 10000);
19422             this._touchViewMask.addClass('show');
19423         }
19424         
19425         this.doTouchViewQuery();
19426         
19427     },
19428     
19429     hideTouchView : function()
19430     {
19431         this.touchViewEl.removeClass(['in','show']);
19432
19433         if(this.animate){
19434             var _this = this;
19435             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19436         }else{
19437             this.touchViewEl.setStyle('display', 'none');
19438         }
19439         
19440         if(this._touchViewMask){
19441             this._touchViewMask.removeClass('show');
19442             Roo.get(document.body).removeClass("x-body-masked");
19443         }
19444     },
19445     
19446     setTouchViewValue : function()
19447     {
19448         if(this.multiple){
19449             this.clearItem();
19450         
19451             var _this = this;
19452
19453             Roo.each(this.tickItems, function(o){
19454                 this.addItem(o);
19455             }, this);
19456         }
19457         
19458         this.hideTouchView();
19459     },
19460     
19461     doTouchViewQuery : function()
19462     {
19463         var qe = {
19464             query: '',
19465             forceAll: true,
19466             combo: this,
19467             cancel:false
19468         };
19469         
19470         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19471             return false;
19472         }
19473         
19474         if(!this.alwaysQuery || this.mode == 'local'){
19475             this.onTouchViewLoad();
19476             return;
19477         }
19478         
19479         this.store.load();
19480     },
19481     
19482     onTouchViewBeforeLoad : function(combo,opts)
19483     {
19484         return;
19485     },
19486
19487     // private
19488     onTouchViewLoad : function()
19489     {
19490         if(this.store.getCount() < 1){
19491             this.onTouchViewEmptyResults();
19492             return;
19493         }
19494         
19495         this.clearTouchView();
19496         
19497         var rawValue = this.getRawValue();
19498         
19499         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19500         
19501         this.tickItems = [];
19502         
19503         this.store.data.each(function(d, rowIndex){
19504             var row = this.touchViewListGroup.createChild(template);
19505             
19506             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19507                 row.addClass(d.data.cls);
19508             }
19509             
19510             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19511                 var cfg = {
19512                     data : d.data,
19513                     html : d.data[this.displayField]
19514                 };
19515                 
19516                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19517                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19518                 }
19519             }
19520             row.removeClass('selected');
19521             if(!this.multiple && this.valueField &&
19522                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19523             {
19524                 // radio buttons..
19525                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19526                 row.addClass('selected');
19527             }
19528             
19529             if(this.multiple && this.valueField &&
19530                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19531             {
19532                 
19533                 // checkboxes...
19534                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19535                 this.tickItems.push(d.data);
19536             }
19537             
19538             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19539             
19540         }, this);
19541         
19542         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19543         
19544         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19545
19546         if(this.modalTitle.length){
19547             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19548         }
19549
19550         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19551         
19552         if(this.mobile_restrict_height && listHeight < bodyHeight){
19553             this.touchViewBodyEl.setHeight(listHeight);
19554         }
19555         
19556         var _this = this;
19557         
19558         if(firstChecked && listHeight > bodyHeight){
19559             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19560         }
19561         
19562     },
19563     
19564     onTouchViewLoadException : function()
19565     {
19566         this.hideTouchView();
19567     },
19568     
19569     onTouchViewEmptyResults : function()
19570     {
19571         this.clearTouchView();
19572         
19573         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19574         
19575         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19576         
19577     },
19578     
19579     clearTouchView : function()
19580     {
19581         this.touchViewListGroup.dom.innerHTML = '';
19582     },
19583     
19584     onTouchViewClick : function(e, el, o)
19585     {
19586         e.preventDefault();
19587         
19588         var row = o.row;
19589         var rowIndex = o.rowIndex;
19590         
19591         var r = this.store.getAt(rowIndex);
19592         
19593         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19594             
19595             if(!this.multiple){
19596                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19597                     c.dom.removeAttribute('checked');
19598                 }, this);
19599
19600                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19601
19602                 this.setFromData(r.data);
19603
19604                 var close = this.closeTriggerEl();
19605
19606                 if(close){
19607                     close.show();
19608                 }
19609
19610                 this.hideTouchView();
19611
19612                 this.fireEvent('select', this, r, rowIndex);
19613
19614                 return;
19615             }
19616
19617             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19618                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19619                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19620                 return;
19621             }
19622
19623             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19624             this.addItem(r.data);
19625             this.tickItems.push(r.data);
19626         }
19627     },
19628     
19629     getAutoCreateNativeIOS : function()
19630     {
19631         var cfg = {
19632             cls: 'form-group' //input-group,
19633         };
19634         
19635         var combobox =  {
19636             tag: 'select',
19637             cls : 'roo-ios-select'
19638         };
19639         
19640         if (this.name) {
19641             combobox.name = this.name;
19642         }
19643         
19644         if (this.disabled) {
19645             combobox.disabled = true;
19646         }
19647         
19648         var settings = this;
19649         
19650         ['xs','sm','md','lg'].map(function(size){
19651             if (settings[size]) {
19652                 cfg.cls += ' col-' + size + '-' + settings[size];
19653             }
19654         });
19655         
19656         cfg.cn = combobox;
19657         
19658         return cfg;
19659         
19660     },
19661     
19662     initIOSView : function()
19663     {
19664         this.store.on('load', this.onIOSViewLoad, this);
19665         
19666         return;
19667     },
19668     
19669     onIOSViewLoad : function()
19670     {
19671         if(this.store.getCount() < 1){
19672             return;
19673         }
19674         
19675         this.clearIOSView();
19676         
19677         if(this.allowBlank) {
19678             
19679             var default_text = '-- SELECT --';
19680             
19681             if(this.placeholder.length){
19682                 default_text = this.placeholder;
19683             }
19684             
19685             if(this.emptyTitle.length){
19686                 default_text += ' - ' + this.emptyTitle + ' -';
19687             }
19688             
19689             var opt = this.inputEl().createChild({
19690                 tag: 'option',
19691                 value : 0,
19692                 html : default_text
19693             });
19694             
19695             var o = {};
19696             o[this.valueField] = 0;
19697             o[this.displayField] = default_text;
19698             
19699             this.ios_options.push({
19700                 data : o,
19701                 el : opt
19702             });
19703             
19704         }
19705         
19706         this.store.data.each(function(d, rowIndex){
19707             
19708             var html = '';
19709             
19710             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19711                 html = d.data[this.displayField];
19712             }
19713             
19714             var value = '';
19715             
19716             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19717                 value = d.data[this.valueField];
19718             }
19719             
19720             var option = {
19721                 tag: 'option',
19722                 value : value,
19723                 html : html
19724             };
19725             
19726             if(this.value == d.data[this.valueField]){
19727                 option['selected'] = true;
19728             }
19729             
19730             var opt = this.inputEl().createChild(option);
19731             
19732             this.ios_options.push({
19733                 data : d.data,
19734                 el : opt
19735             });
19736             
19737         }, this);
19738         
19739         this.inputEl().on('change', function(){
19740            this.fireEvent('select', this);
19741         }, this);
19742         
19743     },
19744     
19745     clearIOSView: function()
19746     {
19747         this.inputEl().dom.innerHTML = '';
19748         
19749         this.ios_options = [];
19750     },
19751     
19752     setIOSValue: function(v)
19753     {
19754         this.value = v;
19755         
19756         if(!this.ios_options){
19757             return;
19758         }
19759         
19760         Roo.each(this.ios_options, function(opts){
19761            
19762            opts.el.dom.removeAttribute('selected');
19763            
19764            if(opts.data[this.valueField] != v){
19765                return;
19766            }
19767            
19768            opts.el.dom.setAttribute('selected', true);
19769            
19770         }, this);
19771     }
19772
19773     /** 
19774     * @cfg {Boolean} grow 
19775     * @hide 
19776     */
19777     /** 
19778     * @cfg {Number} growMin 
19779     * @hide 
19780     */
19781     /** 
19782     * @cfg {Number} growMax 
19783     * @hide 
19784     */
19785     /**
19786      * @hide
19787      * @method autoSize
19788      */
19789 });
19790
19791 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19792     
19793     header : {
19794         tag: 'div',
19795         cls: 'modal-header',
19796         cn: [
19797             {
19798                 tag: 'h4',
19799                 cls: 'modal-title'
19800             }
19801         ]
19802     },
19803     
19804     body : {
19805         tag: 'div',
19806         cls: 'modal-body',
19807         cn: [
19808             {
19809                 tag: 'ul',
19810                 cls: 'list-group'
19811             }
19812         ]
19813     },
19814     
19815     listItemRadio : {
19816         tag: 'li',
19817         cls: 'list-group-item',
19818         cn: [
19819             {
19820                 tag: 'span',
19821                 cls: 'roo-combobox-list-group-item-value'
19822             },
19823             {
19824                 tag: 'div',
19825                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19826                 cn: [
19827                     {
19828                         tag: 'input',
19829                         type: 'radio'
19830                     },
19831                     {
19832                         tag: 'label'
19833                     }
19834                 ]
19835             }
19836         ]
19837     },
19838     
19839     listItemCheckbox : {
19840         tag: 'li',
19841         cls: 'list-group-item',
19842         cn: [
19843             {
19844                 tag: 'span',
19845                 cls: 'roo-combobox-list-group-item-value'
19846             },
19847             {
19848                 tag: 'div',
19849                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19850                 cn: [
19851                     {
19852                         tag: 'input',
19853                         type: 'checkbox'
19854                     },
19855                     {
19856                         tag: 'label'
19857                     }
19858                 ]
19859             }
19860         ]
19861     },
19862     
19863     emptyResult : {
19864         tag: 'div',
19865         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19866     },
19867     
19868     footer : {
19869         tag: 'div',
19870         cls: 'modal-footer',
19871         cn: [
19872             {
19873                 tag: 'div',
19874                 cls: 'row',
19875                 cn: [
19876                     {
19877                         tag: 'div',
19878                         cls: 'col-xs-6 text-left',
19879                         cn: {
19880                             tag: 'button',
19881                             cls: 'btn btn-danger roo-touch-view-cancel',
19882                             html: 'Cancel'
19883                         }
19884                     },
19885                     {
19886                         tag: 'div',
19887                         cls: 'col-xs-6 text-right',
19888                         cn: {
19889                             tag: 'button',
19890                             cls: 'btn btn-success roo-touch-view-ok',
19891                             html: 'OK'
19892                         }
19893                     }
19894                 ]
19895             }
19896         ]
19897         
19898     }
19899 });
19900
19901 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19902     
19903     touchViewTemplate : {
19904         tag: 'div',
19905         cls: 'modal fade roo-combobox-touch-view',
19906         cn: [
19907             {
19908                 tag: 'div',
19909                 cls: 'modal-dialog',
19910                 style : 'position:fixed', // we have to fix position....
19911                 cn: [
19912                     {
19913                         tag: 'div',
19914                         cls: 'modal-content',
19915                         cn: [
19916                             Roo.bootstrap.form.ComboBox.header,
19917                             Roo.bootstrap.form.ComboBox.body,
19918                             Roo.bootstrap.form.ComboBox.footer
19919                         ]
19920                     }
19921                 ]
19922             }
19923         ]
19924     }
19925 });/*
19926  * Based on:
19927  * Ext JS Library 1.1.1
19928  * Copyright(c) 2006-2007, Ext JS, LLC.
19929  *
19930  * Originally Released Under LGPL - original licence link has changed is not relivant.
19931  *
19932  * Fork - LGPL
19933  * <script type="text/javascript">
19934  */
19935
19936 /**
19937  * @class Roo.View
19938  * @extends Roo.util.Observable
19939  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19940  * This class also supports single and multi selection modes. <br>
19941  * Create a data model bound view:
19942  <pre><code>
19943  var store = new Roo.data.Store(...);
19944
19945  var view = new Roo.View({
19946     el : "my-element",
19947     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19948  
19949     singleSelect: true,
19950     selectedClass: "ydataview-selected",
19951     store: store
19952  });
19953
19954  // listen for node click?
19955  view.on("click", function(vw, index, node, e){
19956  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19957  });
19958
19959  // load XML data
19960  dataModel.load("foobar.xml");
19961  </code></pre>
19962  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19963  * <br><br>
19964  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19965  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19966  * 
19967  * Note: old style constructor is still suported (container, template, config)
19968  * 
19969  * @constructor
19970  * Create a new View
19971  * @param {Object} config The config object
19972  * 
19973  */
19974 Roo.View = function(config, depreciated_tpl, depreciated_config){
19975     
19976     this.parent = false;
19977     
19978     if (typeof(depreciated_tpl) == 'undefined') {
19979         // new way.. - universal constructor.
19980         Roo.apply(this, config);
19981         this.el  = Roo.get(this.el);
19982     } else {
19983         // old format..
19984         this.el  = Roo.get(config);
19985         this.tpl = depreciated_tpl;
19986         Roo.apply(this, depreciated_config);
19987     }
19988     this.wrapEl  = this.el.wrap().wrap();
19989     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19990     
19991     
19992     if(typeof(this.tpl) == "string"){
19993         this.tpl = new Roo.Template(this.tpl);
19994     } else {
19995         // support xtype ctors..
19996         this.tpl = new Roo.factory(this.tpl, Roo);
19997     }
19998     
19999     
20000     this.tpl.compile();
20001     
20002     /** @private */
20003     this.addEvents({
20004         /**
20005          * @event beforeclick
20006          * Fires before a click is processed. Returns false to cancel the default action.
20007          * @param {Roo.View} this
20008          * @param {Number} index The index of the target node
20009          * @param {HTMLElement} node The target node
20010          * @param {Roo.EventObject} e The raw event object
20011          */
20012             "beforeclick" : true,
20013         /**
20014          * @event click
20015          * Fires when a template node is clicked.
20016          * @param {Roo.View} this
20017          * @param {Number} index The index of the target node
20018          * @param {HTMLElement} node The target node
20019          * @param {Roo.EventObject} e The raw event object
20020          */
20021             "click" : true,
20022         /**
20023          * @event dblclick
20024          * Fires when a template node is double clicked.
20025          * @param {Roo.View} this
20026          * @param {Number} index The index of the target node
20027          * @param {HTMLElement} node The target node
20028          * @param {Roo.EventObject} e The raw event object
20029          */
20030             "dblclick" : true,
20031         /**
20032          * @event contextmenu
20033          * Fires when a template node is right clicked.
20034          * @param {Roo.View} this
20035          * @param {Number} index The index of the target node
20036          * @param {HTMLElement} node The target node
20037          * @param {Roo.EventObject} e The raw event object
20038          */
20039             "contextmenu" : true,
20040         /**
20041          * @event selectionchange
20042          * Fires when the selected nodes change.
20043          * @param {Roo.View} this
20044          * @param {Array} selections Array of the selected nodes
20045          */
20046             "selectionchange" : true,
20047     
20048         /**
20049          * @event beforeselect
20050          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20051          * @param {Roo.View} this
20052          * @param {HTMLElement} node The node to be selected
20053          * @param {Array} selections Array of currently selected nodes
20054          */
20055             "beforeselect" : true,
20056         /**
20057          * @event preparedata
20058          * Fires on every row to render, to allow you to change the data.
20059          * @param {Roo.View} this
20060          * @param {Object} data to be rendered (change this)
20061          */
20062           "preparedata" : true
20063           
20064           
20065         });
20066
20067
20068
20069     this.el.on({
20070         "click": this.onClick,
20071         "dblclick": this.onDblClick,
20072         "contextmenu": this.onContextMenu,
20073         scope:this
20074     });
20075
20076     this.selections = [];
20077     this.nodes = [];
20078     this.cmp = new Roo.CompositeElementLite([]);
20079     if(this.store){
20080         this.store = Roo.factory(this.store, Roo.data);
20081         this.setStore(this.store, true);
20082     }
20083     
20084     if ( this.footer && this.footer.xtype) {
20085            
20086          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20087         
20088         this.footer.dataSource = this.store;
20089         this.footer.container = fctr;
20090         this.footer = Roo.factory(this.footer, Roo);
20091         fctr.insertFirst(this.el);
20092         
20093         // this is a bit insane - as the paging toolbar seems to detach the el..
20094 //        dom.parentNode.parentNode.parentNode
20095          // they get detached?
20096     }
20097     
20098     
20099     Roo.View.superclass.constructor.call(this);
20100     
20101     
20102 };
20103
20104 Roo.extend(Roo.View, Roo.util.Observable, {
20105     
20106      /**
20107      * @cfg {Roo.data.Store} store Data store to load data from.
20108      */
20109     store : false,
20110     
20111     /**
20112      * @cfg {String|Roo.Element} el The container element.
20113      */
20114     el : '',
20115     
20116     /**
20117      * @cfg {String|Roo.Template} tpl The template used by this View 
20118      */
20119     tpl : false,
20120     /**
20121      * @cfg {String} dataName the named area of the template to use as the data area
20122      *                          Works with domtemplates roo-name="name"
20123      */
20124     dataName: false,
20125     /**
20126      * @cfg {String} selectedClass The css class to add to selected nodes
20127      */
20128     selectedClass : "x-view-selected",
20129      /**
20130      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20131      */
20132     emptyText : "",
20133     
20134     /**
20135      * @cfg {String} text to display on mask (default Loading)
20136      */
20137     mask : false,
20138     /**
20139      * @cfg {Boolean} multiSelect Allow multiple selection
20140      */
20141     multiSelect : false,
20142     /**
20143      * @cfg {Boolean} singleSelect Allow single selection
20144      */
20145     singleSelect:  false,
20146     
20147     /**
20148      * @cfg {Boolean} toggleSelect - selecting 
20149      */
20150     toggleSelect : false,
20151     
20152     /**
20153      * @cfg {Boolean} tickable - selecting 
20154      */
20155     tickable : false,
20156     
20157     /**
20158      * Returns the element this view is bound to.
20159      * @return {Roo.Element}
20160      */
20161     getEl : function(){
20162         return this.wrapEl;
20163     },
20164     
20165     
20166
20167     /**
20168      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20169      */
20170     refresh : function(){
20171         //Roo.log('refresh');
20172         var t = this.tpl;
20173         
20174         // if we are using something like 'domtemplate', then
20175         // the what gets used is:
20176         // t.applySubtemplate(NAME, data, wrapping data..)
20177         // the outer template then get' applied with
20178         //     the store 'extra data'
20179         // and the body get's added to the
20180         //      roo-name="data" node?
20181         //      <span class='roo-tpl-{name}'></span> ?????
20182         
20183         
20184         
20185         this.clearSelections();
20186         this.el.update("");
20187         var html = [];
20188         var records = this.store.getRange();
20189         if(records.length < 1) {
20190             
20191             // is this valid??  = should it render a template??
20192             
20193             this.el.update(this.emptyText);
20194             return;
20195         }
20196         var el = this.el;
20197         if (this.dataName) {
20198             this.el.update(t.apply(this.store.meta)); //????
20199             el = this.el.child('.roo-tpl-' + this.dataName);
20200         }
20201         
20202         for(var i = 0, len = records.length; i < len; i++){
20203             var data = this.prepareData(records[i].data, i, records[i]);
20204             this.fireEvent("preparedata", this, data, i, records[i]);
20205             
20206             var d = Roo.apply({}, data);
20207             
20208             if(this.tickable){
20209                 Roo.apply(d, {'roo-id' : Roo.id()});
20210                 
20211                 var _this = this;
20212             
20213                 Roo.each(this.parent.item, function(item){
20214                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20215                         return;
20216                     }
20217                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20218                 });
20219             }
20220             
20221             html[html.length] = Roo.util.Format.trim(
20222                 this.dataName ?
20223                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20224                     t.apply(d)
20225             );
20226         }
20227         
20228         
20229         
20230         el.update(html.join(""));
20231         this.nodes = el.dom.childNodes;
20232         this.updateIndexes(0);
20233     },
20234     
20235
20236     /**
20237      * Function to override to reformat the data that is sent to
20238      * the template for each node.
20239      * DEPRICATED - use the preparedata event handler.
20240      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20241      * a JSON object for an UpdateManager bound view).
20242      */
20243     prepareData : function(data, index, record)
20244     {
20245         this.fireEvent("preparedata", this, data, index, record);
20246         return data;
20247     },
20248
20249     onUpdate : function(ds, record){
20250         // Roo.log('on update');   
20251         this.clearSelections();
20252         var index = this.store.indexOf(record);
20253         var n = this.nodes[index];
20254         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20255         n.parentNode.removeChild(n);
20256         this.updateIndexes(index, index);
20257     },
20258
20259     
20260     
20261 // --------- FIXME     
20262     onAdd : function(ds, records, index)
20263     {
20264         //Roo.log(['on Add', ds, records, index] );        
20265         this.clearSelections();
20266         if(this.nodes.length == 0){
20267             this.refresh();
20268             return;
20269         }
20270         var n = this.nodes[index];
20271         for(var i = 0, len = records.length; i < len; i++){
20272             var d = this.prepareData(records[i].data, i, records[i]);
20273             if(n){
20274                 this.tpl.insertBefore(n, d);
20275             }else{
20276                 
20277                 this.tpl.append(this.el, d);
20278             }
20279         }
20280         this.updateIndexes(index);
20281     },
20282
20283     onRemove : function(ds, record, index){
20284        // Roo.log('onRemove');
20285         this.clearSelections();
20286         var el = this.dataName  ?
20287             this.el.child('.roo-tpl-' + this.dataName) :
20288             this.el; 
20289         
20290         el.dom.removeChild(this.nodes[index]);
20291         this.updateIndexes(index);
20292     },
20293
20294     /**
20295      * Refresh an individual node.
20296      * @param {Number} index
20297      */
20298     refreshNode : function(index){
20299         this.onUpdate(this.store, this.store.getAt(index));
20300     },
20301
20302     updateIndexes : function(startIndex, endIndex){
20303         var ns = this.nodes;
20304         startIndex = startIndex || 0;
20305         endIndex = endIndex || ns.length - 1;
20306         for(var i = startIndex; i <= endIndex; i++){
20307             ns[i].nodeIndex = i;
20308         }
20309     },
20310
20311     /**
20312      * Changes the data store this view uses and refresh the view.
20313      * @param {Store} store
20314      */
20315     setStore : function(store, initial){
20316         if(!initial && this.store){
20317             this.store.un("datachanged", this.refresh);
20318             this.store.un("add", this.onAdd);
20319             this.store.un("remove", this.onRemove);
20320             this.store.un("update", this.onUpdate);
20321             this.store.un("clear", this.refresh);
20322             this.store.un("beforeload", this.onBeforeLoad);
20323             this.store.un("load", this.onLoad);
20324             this.store.un("loadexception", this.onLoad);
20325         }
20326         if(store){
20327           
20328             store.on("datachanged", this.refresh, this);
20329             store.on("add", this.onAdd, this);
20330             store.on("remove", this.onRemove, this);
20331             store.on("update", this.onUpdate, this);
20332             store.on("clear", this.refresh, this);
20333             store.on("beforeload", this.onBeforeLoad, this);
20334             store.on("load", this.onLoad, this);
20335             store.on("loadexception", this.onLoad, this);
20336         }
20337         
20338         if(store){
20339             this.refresh();
20340         }
20341     },
20342     /**
20343      * onbeforeLoad - masks the loading area.
20344      *
20345      */
20346     onBeforeLoad : function(store,opts)
20347     {
20348          //Roo.log('onBeforeLoad');   
20349         if (!opts.add) {
20350             this.el.update("");
20351         }
20352         this.el.mask(this.mask ? this.mask : "Loading" ); 
20353     },
20354     onLoad : function ()
20355     {
20356         this.el.unmask();
20357     },
20358     
20359
20360     /**
20361      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20362      * @param {HTMLElement} node
20363      * @return {HTMLElement} The template node
20364      */
20365     findItemFromChild : function(node){
20366         var el = this.dataName  ?
20367             this.el.child('.roo-tpl-' + this.dataName,true) :
20368             this.el.dom; 
20369         
20370         if(!node || node.parentNode == el){
20371                     return node;
20372             }
20373             var p = node.parentNode;
20374             while(p && p != el){
20375             if(p.parentNode == el){
20376                 return p;
20377             }
20378             p = p.parentNode;
20379         }
20380             return null;
20381     },
20382
20383     /** @ignore */
20384     onClick : function(e){
20385         var item = this.findItemFromChild(e.getTarget());
20386         if(item){
20387             var index = this.indexOf(item);
20388             if(this.onItemClick(item, index, e) !== false){
20389                 this.fireEvent("click", this, index, item, e);
20390             }
20391         }else{
20392             this.clearSelections();
20393         }
20394     },
20395
20396     /** @ignore */
20397     onContextMenu : function(e){
20398         var item = this.findItemFromChild(e.getTarget());
20399         if(item){
20400             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20401         }
20402     },
20403
20404     /** @ignore */
20405     onDblClick : function(e){
20406         var item = this.findItemFromChild(e.getTarget());
20407         if(item){
20408             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20409         }
20410     },
20411
20412     onItemClick : function(item, index, e)
20413     {
20414         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20415             return false;
20416         }
20417         if (this.toggleSelect) {
20418             var m = this.isSelected(item) ? 'unselect' : 'select';
20419             //Roo.log(m);
20420             var _t = this;
20421             _t[m](item, true, false);
20422             return true;
20423         }
20424         if(this.multiSelect || this.singleSelect){
20425             if(this.multiSelect && e.shiftKey && this.lastSelection){
20426                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20427             }else{
20428                 this.select(item, this.multiSelect && e.ctrlKey);
20429                 this.lastSelection = item;
20430             }
20431             
20432             if(!this.tickable){
20433                 e.preventDefault();
20434             }
20435             
20436         }
20437         return true;
20438     },
20439
20440     /**
20441      * Get the number of selected nodes.
20442      * @return {Number}
20443      */
20444     getSelectionCount : function(){
20445         return this.selections.length;
20446     },
20447
20448     /**
20449      * Get the currently selected nodes.
20450      * @return {Array} An array of HTMLElements
20451      */
20452     getSelectedNodes : function(){
20453         return this.selections;
20454     },
20455
20456     /**
20457      * Get the indexes of the selected nodes.
20458      * @return {Array}
20459      */
20460     getSelectedIndexes : function(){
20461         var indexes = [], s = this.selections;
20462         for(var i = 0, len = s.length; i < len; i++){
20463             indexes.push(s[i].nodeIndex);
20464         }
20465         return indexes;
20466     },
20467
20468     /**
20469      * Clear all selections
20470      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20471      */
20472     clearSelections : function(suppressEvent){
20473         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20474             this.cmp.elements = this.selections;
20475             this.cmp.removeClass(this.selectedClass);
20476             this.selections = [];
20477             if(!suppressEvent){
20478                 this.fireEvent("selectionchange", this, this.selections);
20479             }
20480         }
20481     },
20482
20483     /**
20484      * Returns true if the passed node is selected
20485      * @param {HTMLElement/Number} node The node or node index
20486      * @return {Boolean}
20487      */
20488     isSelected : function(node){
20489         var s = this.selections;
20490         if(s.length < 1){
20491             return false;
20492         }
20493         node = this.getNode(node);
20494         return s.indexOf(node) !== -1;
20495     },
20496
20497     /**
20498      * Selects nodes.
20499      * @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
20500      * @param {Boolean} keepExisting (optional) true to keep existing selections
20501      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20502      */
20503     select : function(nodeInfo, keepExisting, suppressEvent){
20504         if(nodeInfo instanceof Array){
20505             if(!keepExisting){
20506                 this.clearSelections(true);
20507             }
20508             for(var i = 0, len = nodeInfo.length; i < len; i++){
20509                 this.select(nodeInfo[i], true, true);
20510             }
20511             return;
20512         } 
20513         var node = this.getNode(nodeInfo);
20514         if(!node || this.isSelected(node)){
20515             return; // already selected.
20516         }
20517         if(!keepExisting){
20518             this.clearSelections(true);
20519         }
20520         
20521         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20522             Roo.fly(node).addClass(this.selectedClass);
20523             this.selections.push(node);
20524             if(!suppressEvent){
20525                 this.fireEvent("selectionchange", this, this.selections);
20526             }
20527         }
20528         
20529         
20530     },
20531       /**
20532      * Unselects 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 IGNORED (for campatibility with select)
20535      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20536      */
20537     unselect : function(nodeInfo, keepExisting, suppressEvent)
20538     {
20539         if(nodeInfo instanceof Array){
20540             Roo.each(this.selections, function(s) {
20541                 this.unselect(s, nodeInfo);
20542             }, this);
20543             return;
20544         }
20545         var node = this.getNode(nodeInfo);
20546         if(!node || !this.isSelected(node)){
20547             //Roo.log("not selected");
20548             return; // not selected.
20549         }
20550         // fireevent???
20551         var ns = [];
20552         Roo.each(this.selections, function(s) {
20553             if (s == node ) {
20554                 Roo.fly(node).removeClass(this.selectedClass);
20555
20556                 return;
20557             }
20558             ns.push(s);
20559         },this);
20560         
20561         this.selections= ns;
20562         this.fireEvent("selectionchange", this, this.selections);
20563     },
20564
20565     /**
20566      * Gets a template node.
20567      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20568      * @return {HTMLElement} The node or null if it wasn't found
20569      */
20570     getNode : function(nodeInfo){
20571         if(typeof nodeInfo == "string"){
20572             return document.getElementById(nodeInfo);
20573         }else if(typeof nodeInfo == "number"){
20574             return this.nodes[nodeInfo];
20575         }
20576         return nodeInfo;
20577     },
20578
20579     /**
20580      * Gets a range template nodes.
20581      * @param {Number} startIndex
20582      * @param {Number} endIndex
20583      * @return {Array} An array of nodes
20584      */
20585     getNodes : function(start, end){
20586         var ns = this.nodes;
20587         start = start || 0;
20588         end = typeof end == "undefined" ? ns.length - 1 : end;
20589         var nodes = [];
20590         if(start <= end){
20591             for(var i = start; i <= end; i++){
20592                 nodes.push(ns[i]);
20593             }
20594         } else{
20595             for(var i = start; i >= end; i--){
20596                 nodes.push(ns[i]);
20597             }
20598         }
20599         return nodes;
20600     },
20601
20602     /**
20603      * Finds the index of the passed node
20604      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20605      * @return {Number} The index of the node or -1
20606      */
20607     indexOf : function(node){
20608         node = this.getNode(node);
20609         if(typeof node.nodeIndex == "number"){
20610             return node.nodeIndex;
20611         }
20612         var ns = this.nodes;
20613         for(var i = 0, len = ns.length; i < len; i++){
20614             if(ns[i] == node){
20615                 return i;
20616             }
20617         }
20618         return -1;
20619     }
20620 });
20621 /*
20622  * - LGPL
20623  *
20624  * based on jquery fullcalendar
20625  * 
20626  */
20627
20628 Roo.bootstrap = Roo.bootstrap || {};
20629 /**
20630  * @class Roo.bootstrap.Calendar
20631  * @extends Roo.bootstrap.Component
20632  * Bootstrap Calendar class
20633  * @cfg {Boolean} loadMask (true|false) default false
20634  * @cfg {Object} header generate the user specific header of the calendar, default false
20635
20636  * @constructor
20637  * Create a new Container
20638  * @param {Object} config The config object
20639  */
20640
20641
20642
20643 Roo.bootstrap.Calendar = function(config){
20644     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20645      this.addEvents({
20646         /**
20647              * @event select
20648              * Fires when a date is selected
20649              * @param {DatePicker} this
20650              * @param {Date} date The selected date
20651              */
20652         'select': true,
20653         /**
20654              * @event monthchange
20655              * Fires when the displayed month changes 
20656              * @param {DatePicker} this
20657              * @param {Date} date The selected month
20658              */
20659         'monthchange': true,
20660         /**
20661              * @event evententer
20662              * Fires when mouse over an event
20663              * @param {Calendar} this
20664              * @param {event} Event
20665              */
20666         'evententer': true,
20667         /**
20668              * @event eventleave
20669              * Fires when the mouse leaves an
20670              * @param {Calendar} this
20671              * @param {event}
20672              */
20673         'eventleave': true,
20674         /**
20675              * @event eventclick
20676              * Fires when the mouse click an
20677              * @param {Calendar} this
20678              * @param {event}
20679              */
20680         'eventclick': true
20681         
20682     });
20683
20684 };
20685
20686 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20687     
20688           /**
20689      * @cfg {Roo.data.Store} store
20690      * The data source for the calendar
20691      */
20692         store : false,
20693      /**
20694      * @cfg {Number} startDay
20695      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20696      */
20697     startDay : 0,
20698     
20699     loadMask : false,
20700     
20701     header : false,
20702       
20703     getAutoCreate : function(){
20704         
20705         
20706         var fc_button = function(name, corner, style, content ) {
20707             return Roo.apply({},{
20708                 tag : 'span',
20709                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20710                          (corner.length ?
20711                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20712                             ''
20713                         ),
20714                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20715                 unselectable: 'on'
20716             });
20717         };
20718         
20719         var header = {};
20720         
20721         if(!this.header){
20722             header = {
20723                 tag : 'table',
20724                 cls : 'fc-header',
20725                 style : 'width:100%',
20726                 cn : [
20727                     {
20728                         tag: 'tr',
20729                         cn : [
20730                             {
20731                                 tag : 'td',
20732                                 cls : 'fc-header-left',
20733                                 cn : [
20734                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20735                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20736                                     { tag: 'span', cls: 'fc-header-space' },
20737                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20738
20739
20740                                 ]
20741                             },
20742
20743                             {
20744                                 tag : 'td',
20745                                 cls : 'fc-header-center',
20746                                 cn : [
20747                                     {
20748                                         tag: 'span',
20749                                         cls: 'fc-header-title',
20750                                         cn : {
20751                                             tag: 'H2',
20752                                             html : 'month / year'
20753                                         }
20754                                     }
20755
20756                                 ]
20757                             },
20758                             {
20759                                 tag : 'td',
20760                                 cls : 'fc-header-right',
20761                                 cn : [
20762                               /*      fc_button('month', 'left', '', 'month' ),
20763                                     fc_button('week', '', '', 'week' ),
20764                                     fc_button('day', 'right', '', 'day' )
20765                                 */    
20766
20767                                 ]
20768                             }
20769
20770                         ]
20771                     }
20772                 ]
20773             };
20774         }
20775         
20776         header = this.header;
20777         
20778        
20779         var cal_heads = function() {
20780             var ret = [];
20781             // fixme - handle this.
20782             
20783             for (var i =0; i < Date.dayNames.length; i++) {
20784                 var d = Date.dayNames[i];
20785                 ret.push({
20786                     tag: 'th',
20787                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20788                     html : d.substring(0,3)
20789                 });
20790                 
20791             }
20792             ret[0].cls += ' fc-first';
20793             ret[6].cls += ' fc-last';
20794             return ret;
20795         };
20796         var cal_cell = function(n) {
20797             return  {
20798                 tag: 'td',
20799                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20800                 cn : [
20801                     {
20802                         cn : [
20803                             {
20804                                 cls: 'fc-day-number',
20805                                 html: 'D'
20806                             },
20807                             {
20808                                 cls: 'fc-day-content',
20809                              
20810                                 cn : [
20811                                      {
20812                                         style: 'position: relative;' // height: 17px;
20813                                     }
20814                                 ]
20815                             }
20816                             
20817                             
20818                         ]
20819                     }
20820                 ]
20821                 
20822             }
20823         };
20824         var cal_rows = function() {
20825             
20826             var ret = [];
20827             for (var r = 0; r < 6; r++) {
20828                 var row= {
20829                     tag : 'tr',
20830                     cls : 'fc-week',
20831                     cn : []
20832                 };
20833                 
20834                 for (var i =0; i < Date.dayNames.length; i++) {
20835                     var d = Date.dayNames[i];
20836                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20837
20838                 }
20839                 row.cn[0].cls+=' fc-first';
20840                 row.cn[0].cn[0].style = 'min-height:90px';
20841                 row.cn[6].cls+=' fc-last';
20842                 ret.push(row);
20843                 
20844             }
20845             ret[0].cls += ' fc-first';
20846             ret[4].cls += ' fc-prev-last';
20847             ret[5].cls += ' fc-last';
20848             return ret;
20849             
20850         };
20851         
20852         var cal_table = {
20853             tag: 'table',
20854             cls: 'fc-border-separate',
20855             style : 'width:100%',
20856             cellspacing  : 0,
20857             cn : [
20858                 { 
20859                     tag: 'thead',
20860                     cn : [
20861                         { 
20862                             tag: 'tr',
20863                             cls : 'fc-first fc-last',
20864                             cn : cal_heads()
20865                         }
20866                     ]
20867                 },
20868                 { 
20869                     tag: 'tbody',
20870                     cn : cal_rows()
20871                 }
20872                   
20873             ]
20874         };
20875          
20876          var cfg = {
20877             cls : 'fc fc-ltr',
20878             cn : [
20879                 header,
20880                 {
20881                     cls : 'fc-content',
20882                     style : "position: relative;",
20883                     cn : [
20884                         {
20885                             cls : 'fc-view fc-view-month fc-grid',
20886                             style : 'position: relative',
20887                             unselectable : 'on',
20888                             cn : [
20889                                 {
20890                                     cls : 'fc-event-container',
20891                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20892                                 },
20893                                 cal_table
20894                             ]
20895                         }
20896                     ]
20897     
20898                 }
20899            ] 
20900             
20901         };
20902         
20903          
20904         
20905         return cfg;
20906     },
20907     
20908     
20909     initEvents : function()
20910     {
20911         if(!this.store){
20912             throw "can not find store for calendar";
20913         }
20914         
20915         var mark = {
20916             tag: "div",
20917             cls:"x-dlg-mask",
20918             style: "text-align:center",
20919             cn: [
20920                 {
20921                     tag: "div",
20922                     style: "background-color:white;width:50%;margin:250 auto",
20923                     cn: [
20924                         {
20925                             tag: "img",
20926                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20927                         },
20928                         {
20929                             tag: "span",
20930                             html: "Loading"
20931                         }
20932                         
20933                     ]
20934                 }
20935             ]
20936         };
20937         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20938         
20939         var size = this.el.select('.fc-content', true).first().getSize();
20940         this.maskEl.setSize(size.width, size.height);
20941         this.maskEl.enableDisplayMode("block");
20942         if(!this.loadMask){
20943             this.maskEl.hide();
20944         }
20945         
20946         this.store = Roo.factory(this.store, Roo.data);
20947         this.store.on('load', this.onLoad, this);
20948         this.store.on('beforeload', this.onBeforeLoad, this);
20949         
20950         this.resize();
20951         
20952         this.cells = this.el.select('.fc-day',true);
20953         //Roo.log(this.cells);
20954         this.textNodes = this.el.query('.fc-day-number');
20955         this.cells.addClassOnOver('fc-state-hover');
20956         
20957         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20958         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20959         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20960         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20961         
20962         this.on('monthchange', this.onMonthChange, this);
20963         
20964         this.update(new Date().clearTime());
20965     },
20966     
20967     resize : function() {
20968         var sz  = this.el.getSize();
20969         
20970         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20971         this.el.select('.fc-day-content div',true).setHeight(34);
20972     },
20973     
20974     
20975     // private
20976     showPrevMonth : function(e){
20977         this.update(this.activeDate.add("mo", -1));
20978     },
20979     showToday : function(e){
20980         this.update(new Date().clearTime());
20981     },
20982     // private
20983     showNextMonth : function(e){
20984         this.update(this.activeDate.add("mo", 1));
20985     },
20986
20987     // private
20988     showPrevYear : function(){
20989         this.update(this.activeDate.add("y", -1));
20990     },
20991
20992     // private
20993     showNextYear : function(){
20994         this.update(this.activeDate.add("y", 1));
20995     },
20996
20997     
20998    // private
20999     update : function(date)
21000     {
21001         var vd = this.activeDate;
21002         this.activeDate = date;
21003 //        if(vd && this.el){
21004 //            var t = date.getTime();
21005 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21006 //                Roo.log('using add remove');
21007 //                
21008 //                this.fireEvent('monthchange', this, date);
21009 //                
21010 //                this.cells.removeClass("fc-state-highlight");
21011 //                this.cells.each(function(c){
21012 //                   if(c.dateValue == t){
21013 //                       c.addClass("fc-state-highlight");
21014 //                       setTimeout(function(){
21015 //                            try{c.dom.firstChild.focus();}catch(e){}
21016 //                       }, 50);
21017 //                       return false;
21018 //                   }
21019 //                   return true;
21020 //                });
21021 //                return;
21022 //            }
21023 //        }
21024         
21025         var days = date.getDaysInMonth();
21026         
21027         var firstOfMonth = date.getFirstDateOfMonth();
21028         var startingPos = firstOfMonth.getDay()-this.startDay;
21029         
21030         if(startingPos < this.startDay){
21031             startingPos += 7;
21032         }
21033         
21034         var pm = date.add(Date.MONTH, -1);
21035         var prevStart = pm.getDaysInMonth()-startingPos;
21036 //        
21037         this.cells = this.el.select('.fc-day',true);
21038         this.textNodes = this.el.query('.fc-day-number');
21039         this.cells.addClassOnOver('fc-state-hover');
21040         
21041         var cells = this.cells.elements;
21042         var textEls = this.textNodes;
21043         
21044         Roo.each(cells, function(cell){
21045             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21046         });
21047         
21048         days += startingPos;
21049
21050         // convert everything to numbers so it's fast
21051         var day = 86400000;
21052         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21053         //Roo.log(d);
21054         //Roo.log(pm);
21055         //Roo.log(prevStart);
21056         
21057         var today = new Date().clearTime().getTime();
21058         var sel = date.clearTime().getTime();
21059         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21060         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21061         var ddMatch = this.disabledDatesRE;
21062         var ddText = this.disabledDatesText;
21063         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21064         var ddaysText = this.disabledDaysText;
21065         var format = this.format;
21066         
21067         var setCellClass = function(cal, cell){
21068             cell.row = 0;
21069             cell.events = [];
21070             cell.more = [];
21071             //Roo.log('set Cell Class');
21072             cell.title = "";
21073             var t = d.getTime();
21074             
21075             //Roo.log(d);
21076             
21077             cell.dateValue = t;
21078             if(t == today){
21079                 cell.className += " fc-today";
21080                 cell.className += " fc-state-highlight";
21081                 cell.title = cal.todayText;
21082             }
21083             if(t == sel){
21084                 // disable highlight in other month..
21085                 //cell.className += " fc-state-highlight";
21086                 
21087             }
21088             // disabling
21089             if(t < min) {
21090                 cell.className = " fc-state-disabled";
21091                 cell.title = cal.minText;
21092                 return;
21093             }
21094             if(t > max) {
21095                 cell.className = " fc-state-disabled";
21096                 cell.title = cal.maxText;
21097                 return;
21098             }
21099             if(ddays){
21100                 if(ddays.indexOf(d.getDay()) != -1){
21101                     cell.title = ddaysText;
21102                     cell.className = " fc-state-disabled";
21103                 }
21104             }
21105             if(ddMatch && format){
21106                 var fvalue = d.dateFormat(format);
21107                 if(ddMatch.test(fvalue)){
21108                     cell.title = ddText.replace("%0", fvalue);
21109                     cell.className = " fc-state-disabled";
21110                 }
21111             }
21112             
21113             if (!cell.initialClassName) {
21114                 cell.initialClassName = cell.dom.className;
21115             }
21116             
21117             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21118         };
21119
21120         var i = 0;
21121         
21122         for(; i < startingPos; i++) {
21123             textEls[i].innerHTML = (++prevStart);
21124             d.setDate(d.getDate()+1);
21125             
21126             cells[i].className = "fc-past fc-other-month";
21127             setCellClass(this, cells[i]);
21128         }
21129         
21130         var intDay = 0;
21131         
21132         for(; i < days; i++){
21133             intDay = i - startingPos + 1;
21134             textEls[i].innerHTML = (intDay);
21135             d.setDate(d.getDate()+1);
21136             
21137             cells[i].className = ''; // "x-date-active";
21138             setCellClass(this, cells[i]);
21139         }
21140         var extraDays = 0;
21141         
21142         for(; i < 42; i++) {
21143             textEls[i].innerHTML = (++extraDays);
21144             d.setDate(d.getDate()+1);
21145             
21146             cells[i].className = "fc-future fc-other-month";
21147             setCellClass(this, cells[i]);
21148         }
21149         
21150         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21151         
21152         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21153         
21154         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21155         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21156         
21157         if(totalRows != 6){
21158             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21159             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21160         }
21161         
21162         this.fireEvent('monthchange', this, date);
21163         
21164         
21165         /*
21166         if(!this.internalRender){
21167             var main = this.el.dom.firstChild;
21168             var w = main.offsetWidth;
21169             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21170             Roo.fly(main).setWidth(w);
21171             this.internalRender = true;
21172             // opera does not respect the auto grow header center column
21173             // then, after it gets a width opera refuses to recalculate
21174             // without a second pass
21175             if(Roo.isOpera && !this.secondPass){
21176                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21177                 this.secondPass = true;
21178                 this.update.defer(10, this, [date]);
21179             }
21180         }
21181         */
21182         
21183     },
21184     
21185     findCell : function(dt) {
21186         dt = dt.clearTime().getTime();
21187         var ret = false;
21188         this.cells.each(function(c){
21189             //Roo.log("check " +c.dateValue + '?=' + dt);
21190             if(c.dateValue == dt){
21191                 ret = c;
21192                 return false;
21193             }
21194             return true;
21195         });
21196         
21197         return ret;
21198     },
21199     
21200     findCells : function(ev) {
21201         var s = ev.start.clone().clearTime().getTime();
21202        // Roo.log(s);
21203         var e= ev.end.clone().clearTime().getTime();
21204        // Roo.log(e);
21205         var ret = [];
21206         this.cells.each(function(c){
21207              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21208             
21209             if(c.dateValue > e){
21210                 return ;
21211             }
21212             if(c.dateValue < s){
21213                 return ;
21214             }
21215             ret.push(c);
21216         });
21217         
21218         return ret;    
21219     },
21220     
21221 //    findBestRow: function(cells)
21222 //    {
21223 //        var ret = 0;
21224 //        
21225 //        for (var i =0 ; i < cells.length;i++) {
21226 //            ret  = Math.max(cells[i].rows || 0,ret);
21227 //        }
21228 //        return ret;
21229 //        
21230 //    },
21231     
21232     
21233     addItem : function(ev)
21234     {
21235         // look for vertical location slot in
21236         var cells = this.findCells(ev);
21237         
21238 //        ev.row = this.findBestRow(cells);
21239         
21240         // work out the location.
21241         
21242         var crow = false;
21243         var rows = [];
21244         for(var i =0; i < cells.length; i++) {
21245             
21246             cells[i].row = cells[0].row;
21247             
21248             if(i == 0){
21249                 cells[i].row = cells[i].row + 1;
21250             }
21251             
21252             if (!crow) {
21253                 crow = {
21254                     start : cells[i],
21255                     end :  cells[i]
21256                 };
21257                 continue;
21258             }
21259             if (crow.start.getY() == cells[i].getY()) {
21260                 // on same row.
21261                 crow.end = cells[i];
21262                 continue;
21263             }
21264             // different row.
21265             rows.push(crow);
21266             crow = {
21267                 start: cells[i],
21268                 end : cells[i]
21269             };
21270             
21271         }
21272         
21273         rows.push(crow);
21274         ev.els = [];
21275         ev.rows = rows;
21276         ev.cells = cells;
21277         
21278         cells[0].events.push(ev);
21279         
21280         this.calevents.push(ev);
21281     },
21282     
21283     clearEvents: function() {
21284         
21285         if(!this.calevents){
21286             return;
21287         }
21288         
21289         Roo.each(this.cells.elements, function(c){
21290             c.row = 0;
21291             c.events = [];
21292             c.more = [];
21293         });
21294         
21295         Roo.each(this.calevents, function(e) {
21296             Roo.each(e.els, function(el) {
21297                 el.un('mouseenter' ,this.onEventEnter, this);
21298                 el.un('mouseleave' ,this.onEventLeave, this);
21299                 el.remove();
21300             },this);
21301         },this);
21302         
21303         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21304             e.remove();
21305         });
21306         
21307     },
21308     
21309     renderEvents: function()
21310     {   
21311         var _this = this;
21312         
21313         this.cells.each(function(c) {
21314             
21315             if(c.row < 5){
21316                 return;
21317             }
21318             
21319             var ev = c.events;
21320             
21321             var r = 4;
21322             if(c.row != c.events.length){
21323                 r = 4 - (4 - (c.row - c.events.length));
21324             }
21325             
21326             c.events = ev.slice(0, r);
21327             c.more = ev.slice(r);
21328             
21329             if(c.more.length && c.more.length == 1){
21330                 c.events.push(c.more.pop());
21331             }
21332             
21333             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21334             
21335         });
21336             
21337         this.cells.each(function(c) {
21338             
21339             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21340             
21341             
21342             for (var e = 0; e < c.events.length; e++){
21343                 var ev = c.events[e];
21344                 var rows = ev.rows;
21345                 
21346                 for(var i = 0; i < rows.length; i++) {
21347                 
21348                     // how many rows should it span..
21349
21350                     var  cfg = {
21351                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21352                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21353
21354                         unselectable : "on",
21355                         cn : [
21356                             {
21357                                 cls: 'fc-event-inner',
21358                                 cn : [
21359     //                                {
21360     //                                  tag:'span',
21361     //                                  cls: 'fc-event-time',
21362     //                                  html : cells.length > 1 ? '' : ev.time
21363     //                                },
21364                                     {
21365                                       tag:'span',
21366                                       cls: 'fc-event-title',
21367                                       html : String.format('{0}', ev.title)
21368                                     }
21369
21370
21371                                 ]
21372                             },
21373                             {
21374                                 cls: 'ui-resizable-handle ui-resizable-e',
21375                                 html : '&nbsp;&nbsp;&nbsp'
21376                             }
21377
21378                         ]
21379                     };
21380
21381                     if (i == 0) {
21382                         cfg.cls += ' fc-event-start';
21383                     }
21384                     if ((i+1) == rows.length) {
21385                         cfg.cls += ' fc-event-end';
21386                     }
21387
21388                     var ctr = _this.el.select('.fc-event-container',true).first();
21389                     var cg = ctr.createChild(cfg);
21390
21391                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21392                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21393
21394                     var r = (c.more.length) ? 1 : 0;
21395                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21396                     cg.setWidth(ebox.right - sbox.x -2);
21397
21398                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21399                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21400                     cg.on('click', _this.onEventClick, _this, ev);
21401
21402                     ev.els.push(cg);
21403                     
21404                 }
21405                 
21406             }
21407             
21408             
21409             if(c.more.length){
21410                 var  cfg = {
21411                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21412                     style : 'position: absolute',
21413                     unselectable : "on",
21414                     cn : [
21415                         {
21416                             cls: 'fc-event-inner',
21417                             cn : [
21418                                 {
21419                                   tag:'span',
21420                                   cls: 'fc-event-title',
21421                                   html : 'More'
21422                                 }
21423
21424
21425                             ]
21426                         },
21427                         {
21428                             cls: 'ui-resizable-handle ui-resizable-e',
21429                             html : '&nbsp;&nbsp;&nbsp'
21430                         }
21431
21432                     ]
21433                 };
21434
21435                 var ctr = _this.el.select('.fc-event-container',true).first();
21436                 var cg = ctr.createChild(cfg);
21437
21438                 var sbox = c.select('.fc-day-content',true).first().getBox();
21439                 var ebox = c.select('.fc-day-content',true).first().getBox();
21440                 //Roo.log(cg);
21441                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21442                 cg.setWidth(ebox.right - sbox.x -2);
21443
21444                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21445                 
21446             }
21447             
21448         });
21449         
21450         
21451         
21452     },
21453     
21454     onEventEnter: function (e, el,event,d) {
21455         this.fireEvent('evententer', this, el, event);
21456     },
21457     
21458     onEventLeave: function (e, el,event,d) {
21459         this.fireEvent('eventleave', this, el, event);
21460     },
21461     
21462     onEventClick: function (e, el,event,d) {
21463         this.fireEvent('eventclick', this, el, event);
21464     },
21465     
21466     onMonthChange: function () {
21467         this.store.load();
21468     },
21469     
21470     onMoreEventClick: function(e, el, more)
21471     {
21472         var _this = this;
21473         
21474         this.calpopover.placement = 'right';
21475         this.calpopover.setTitle('More');
21476         
21477         this.calpopover.setContent('');
21478         
21479         var ctr = this.calpopover.el.select('.popover-content', true).first();
21480         
21481         Roo.each(more, function(m){
21482             var cfg = {
21483                 cls : 'fc-event-hori fc-event-draggable',
21484                 html : m.title
21485             };
21486             var cg = ctr.createChild(cfg);
21487             
21488             cg.on('click', _this.onEventClick, _this, m);
21489         });
21490         
21491         this.calpopover.show(el);
21492         
21493         
21494     },
21495     
21496     onLoad: function () 
21497     {   
21498         this.calevents = [];
21499         var cal = this;
21500         
21501         if(this.store.getCount() > 0){
21502             this.store.data.each(function(d){
21503                cal.addItem({
21504                     id : d.data.id,
21505                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21506                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21507                     time : d.data.start_time,
21508                     title : d.data.title,
21509                     description : d.data.description,
21510                     venue : d.data.venue
21511                 });
21512             });
21513         }
21514         
21515         this.renderEvents();
21516         
21517         if(this.calevents.length && this.loadMask){
21518             this.maskEl.hide();
21519         }
21520     },
21521     
21522     onBeforeLoad: function()
21523     {
21524         this.clearEvents();
21525         if(this.loadMask){
21526             this.maskEl.show();
21527         }
21528     }
21529 });
21530
21531  
21532  /*
21533  * - LGPL
21534  *
21535  * element
21536  * 
21537  */
21538
21539 /**
21540  * @class Roo.bootstrap.Popover
21541  * @extends Roo.bootstrap.Component
21542  * @parent none builder
21543  * @children Roo.bootstrap.Component
21544  * Bootstrap Popover class
21545  * @cfg {String} html contents of the popover   (or false to use children..)
21546  * @cfg {String} title of popover (or false to hide)
21547  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21548  * @cfg {String} trigger click || hover (or false to trigger manually)
21549  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21550  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21551  *      - if false and it has a 'parent' then it will be automatically added to that element
21552  *      - if string - Roo.get  will be called 
21553  * @cfg {Number} delay - delay before showing
21554  
21555  * @constructor
21556  * Create a new Popover
21557  * @param {Object} config The config object
21558  */
21559
21560 Roo.bootstrap.Popover = function(config){
21561     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21562     
21563     this.addEvents({
21564         // raw events
21565          /**
21566          * @event show
21567          * After the popover show
21568          * 
21569          * @param {Roo.bootstrap.Popover} this
21570          */
21571         "show" : true,
21572         /**
21573          * @event hide
21574          * After the popover hide
21575          * 
21576          * @param {Roo.bootstrap.Popover} this
21577          */
21578         "hide" : true
21579     });
21580 };
21581
21582 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21583     
21584     title: false,
21585     html: false,
21586     
21587     placement : 'right',
21588     trigger : 'hover', // hover
21589     modal : false,
21590     delay : 0,
21591     
21592     over: false,
21593     
21594     can_build_overlaid : false,
21595     
21596     maskEl : false, // the mask element
21597     headerEl : false,
21598     contentEl : false,
21599     alignEl : false, // when show is called with an element - this get's stored.
21600     
21601     getChildContainer : function()
21602     {
21603         return this.contentEl;
21604         
21605     },
21606     getPopoverHeader : function()
21607     {
21608         this.title = true; // flag not to hide it..
21609         this.headerEl.addClass('p-0');
21610         return this.headerEl
21611     },
21612     
21613     
21614     getAutoCreate : function(){
21615          
21616         var cfg = {
21617            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21618            style: 'display:block',
21619            cn : [
21620                 {
21621                     cls : 'arrow'
21622                 },
21623                 {
21624                     cls : 'popover-inner ',
21625                     cn : [
21626                         {
21627                             tag: 'h3',
21628                             cls: 'popover-title popover-header',
21629                             html : this.title === false ? '' : this.title
21630                         },
21631                         {
21632                             cls : 'popover-content popover-body '  + (this.cls || ''),
21633                             html : this.html || ''
21634                         }
21635                     ]
21636                     
21637                 }
21638            ]
21639         };
21640         
21641         return cfg;
21642     },
21643     /**
21644      * @param {string} the title
21645      */
21646     setTitle: function(str)
21647     {
21648         this.title = str;
21649         if (this.el) {
21650             this.headerEl.dom.innerHTML = str;
21651         }
21652         
21653     },
21654     /**
21655      * @param {string} the body content
21656      */
21657     setContent: function(str)
21658     {
21659         this.html = str;
21660         if (this.contentEl) {
21661             this.contentEl.dom.innerHTML = str;
21662         }
21663         
21664     },
21665     // as it get's added to the bottom of the page.
21666     onRender : function(ct, position)
21667     {
21668         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21669         
21670         
21671         
21672         if(!this.el){
21673             var cfg = Roo.apply({},  this.getAutoCreate());
21674             cfg.id = Roo.id();
21675             
21676             if (this.cls) {
21677                 cfg.cls += ' ' + this.cls;
21678             }
21679             if (this.style) {
21680                 cfg.style = this.style;
21681             }
21682             //Roo.log("adding to ");
21683             this.el = Roo.get(document.body).createChild(cfg, position);
21684 //            Roo.log(this.el);
21685         }
21686         
21687         this.contentEl = this.el.select('.popover-content',true).first();
21688         this.headerEl =  this.el.select('.popover-title',true).first();
21689         
21690         var nitems = [];
21691         if(typeof(this.items) != 'undefined'){
21692             var items = this.items;
21693             delete this.items;
21694
21695             for(var i =0;i < items.length;i++) {
21696                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21697             }
21698         }
21699
21700         this.items = nitems;
21701         
21702         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21703         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21704         
21705         
21706         
21707         this.initEvents();
21708     },
21709     
21710     resizeMask : function()
21711     {
21712         this.maskEl.setSize(
21713             Roo.lib.Dom.getViewWidth(true),
21714             Roo.lib.Dom.getViewHeight(true)
21715         );
21716     },
21717     
21718     initEvents : function()
21719     {
21720         
21721         if (!this.modal) { 
21722             Roo.bootstrap.Popover.register(this);
21723         }
21724          
21725         this.arrowEl = this.el.select('.arrow',true).first();
21726         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21727         this.el.enableDisplayMode('block');
21728         this.el.hide();
21729  
21730         
21731         if (this.over === false && !this.parent()) {
21732             return; 
21733         }
21734         if (this.triggers === false) {
21735             return;
21736         }
21737          
21738         // support parent
21739         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21740         var triggers = this.trigger ? this.trigger.split(' ') : [];
21741         Roo.each(triggers, function(trigger) {
21742         
21743             if (trigger == 'click') {
21744                 on_el.on('click', this.toggle, this);
21745             } else if (trigger != 'manual') {
21746                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21747                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21748       
21749                 on_el.on(eventIn  ,this.enter, this);
21750                 on_el.on(eventOut, this.leave, this);
21751             }
21752         }, this);
21753     },
21754     
21755     
21756     // private
21757     timeout : null,
21758     hoverState : null,
21759     
21760     toggle : function () {
21761         this.hoverState == 'in' ? this.leave() : this.enter();
21762     },
21763     
21764     enter : function () {
21765         
21766         clearTimeout(this.timeout);
21767     
21768         this.hoverState = 'in';
21769     
21770         if (!this.delay || !this.delay.show) {
21771             this.show();
21772             return;
21773         }
21774         var _t = this;
21775         this.timeout = setTimeout(function () {
21776             if (_t.hoverState == 'in') {
21777                 _t.show();
21778             }
21779         }, this.delay.show)
21780     },
21781     
21782     leave : function() {
21783         clearTimeout(this.timeout);
21784     
21785         this.hoverState = 'out';
21786     
21787         if (!this.delay || !this.delay.hide) {
21788             this.hide();
21789             return;
21790         }
21791         var _t = this;
21792         this.timeout = setTimeout(function () {
21793             if (_t.hoverState == 'out') {
21794                 _t.hide();
21795             }
21796         }, this.delay.hide)
21797     },
21798     
21799     /**
21800      * update the position of the dialog
21801      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21802      * 
21803      *
21804      */
21805     
21806     doAlign : function()
21807     {
21808         
21809         if (this.alignEl) {
21810             this.updatePosition(this.placement, true);
21811              
21812         } else {
21813             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21814             var es = this.el.getSize();
21815             var x = Roo.lib.Dom.getViewWidth()/2;
21816             var y = Roo.lib.Dom.getViewHeight()/2;
21817             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21818             
21819         }
21820
21821          
21822          
21823         
21824         
21825     },
21826     
21827     /**
21828      * Show the popover
21829      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21830      * @param {string} (left|right|top|bottom) position
21831      */
21832     show : function (on_el, placement)
21833     {
21834         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21835         on_el = on_el || false; // default to false
21836          
21837         if (!on_el) {
21838             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21839                 on_el = this.parent().el;
21840             } else if (this.over) {
21841                 on_el = Roo.get(this.over);
21842             }
21843             
21844         }
21845         
21846         this.alignEl = Roo.get( on_el );
21847
21848         if (!this.el) {
21849             this.render(document.body);
21850         }
21851         
21852         
21853          
21854         
21855         if (this.title === false) {
21856             this.headerEl.hide();
21857         }
21858         
21859        
21860         this.el.show();
21861         this.el.dom.style.display = 'block';
21862          
21863         this.doAlign();
21864         
21865         //var arrow = this.el.select('.arrow',true).first();
21866         //arrow.set(align[2], 
21867         
21868         this.el.addClass('in');
21869         
21870          
21871         
21872         this.hoverState = 'in';
21873         
21874         if (this.modal) {
21875             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21876             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21877             this.maskEl.dom.style.display = 'block';
21878             this.maskEl.addClass('show');
21879         }
21880         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21881  
21882         this.fireEvent('show', this);
21883         
21884     },
21885     /**
21886      * fire this manually after loading a grid in the table for example
21887      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21888      * @param {Boolean} try and move it if we cant get right position.
21889      */
21890     updatePosition : function(placement, try_move)
21891     {
21892         // allow for calling with no parameters
21893         placement = placement   ? placement :  this.placement;
21894         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21895         
21896         this.el.removeClass([
21897             'fade','top','bottom', 'left', 'right','in',
21898             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21899         ]);
21900         this.el.addClass(placement + ' bs-popover-' + placement);
21901         
21902         if (!this.alignEl ) {
21903             return false;
21904         }
21905         
21906         switch (placement) {
21907             case 'right':
21908                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21909                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21910                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21911                     //normal display... or moved up/down.
21912                     this.el.setXY(offset);
21913                     var xy = this.alignEl.getAnchorXY('tr', false);
21914                     xy[0]+=2;xy[1]+=5;
21915                     this.arrowEl.setXY(xy);
21916                     return true;
21917                 }
21918                 // continue through...
21919                 return this.updatePosition('left', false);
21920                 
21921             
21922             case 'left':
21923                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21924                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21925                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21926                     //normal display... or moved up/down.
21927                     this.el.setXY(offset);
21928                     var xy = this.alignEl.getAnchorXY('tl', false);
21929                     xy[0]-=10;xy[1]+=5; // << fix me
21930                     this.arrowEl.setXY(xy);
21931                     return true;
21932                 }
21933                 // call self...
21934                 return this.updatePosition('right', false);
21935             
21936             case 'top':
21937                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21938                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21939                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21940                     //normal display... or moved up/down.
21941                     this.el.setXY(offset);
21942                     var xy = this.alignEl.getAnchorXY('t', false);
21943                     xy[1]-=10; // << fix me
21944                     this.arrowEl.setXY(xy);
21945                     return true;
21946                 }
21947                 // fall through
21948                return this.updatePosition('bottom', false);
21949             
21950             case 'bottom':
21951                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21952                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21953                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21954                     //normal display... or moved up/down.
21955                     this.el.setXY(offset);
21956                     var xy = this.alignEl.getAnchorXY('b', false);
21957                      xy[1]+=2; // << fix me
21958                     this.arrowEl.setXY(xy);
21959                     return true;
21960                 }
21961                 // fall through
21962                 return this.updatePosition('top', false);
21963                 
21964             
21965         }
21966         
21967         
21968         return false;
21969     },
21970     
21971     hide : function()
21972     {
21973         this.el.setXY([0,0]);
21974         this.el.removeClass('in');
21975         this.el.hide();
21976         this.hoverState = null;
21977         this.maskEl.hide(); // always..
21978         this.fireEvent('hide', this);
21979     }
21980     
21981 });
21982
21983
21984 Roo.apply(Roo.bootstrap.Popover, {
21985
21986     alignment : {
21987         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21988         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21989         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21990         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21991     },
21992     
21993     zIndex : 20001,
21994
21995     clickHander : false,
21996     
21997     
21998
21999     onMouseDown : function(e)
22000     {
22001         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
22002             /// what is nothing is showing..
22003             this.hideAll();
22004         }
22005          
22006     },
22007     
22008     
22009     popups : [],
22010     
22011     register : function(popup)
22012     {
22013         if (!Roo.bootstrap.Popover.clickHandler) {
22014             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22015         }
22016         // hide other popups.
22017         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22018         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22019         this.hideAll(); //<< why?
22020         //this.popups.push(popup);
22021     },
22022     hideAll : function()
22023     {
22024         this.popups.forEach(function(p) {
22025             p.hide();
22026         });
22027     },
22028     onShow : function() {
22029         Roo.bootstrap.Popover.popups.push(this);
22030     },
22031     onHide : function() {
22032         Roo.bootstrap.Popover.popups.remove(this);
22033     } 
22034
22035 });
22036 /**
22037  * @class Roo.bootstrap.PopoverNav
22038  * @extends Roo.bootstrap.nav.Simplebar
22039  * @parent Roo.bootstrap.Popover
22040  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22041  * @licence LGPL
22042  * Bootstrap Popover header navigation class
22043  * FIXME? should this go under nav?
22044  *
22045  * 
22046  * @constructor
22047  * Create a new Popover Header Navigation 
22048  * @param {Object} config The config object
22049  */
22050
22051 Roo.bootstrap.PopoverNav = function(config){
22052     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22053 };
22054
22055 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22056     
22057     
22058     container_method : 'getPopoverHeader' 
22059     
22060      
22061     
22062     
22063    
22064 });
22065
22066  
22067
22068  /*
22069  * - LGPL
22070  *
22071  * Progress
22072  * 
22073  */
22074
22075 /**
22076  * @class Roo.bootstrap.Progress
22077  * @extends Roo.bootstrap.Component
22078  * @children Roo.bootstrap.ProgressBar
22079  * Bootstrap Progress class
22080  * @cfg {Boolean} striped striped of the progress bar
22081  * @cfg {Boolean} active animated of the progress bar
22082  * 
22083  * 
22084  * @constructor
22085  * Create a new Progress
22086  * @param {Object} config The config object
22087  */
22088
22089 Roo.bootstrap.Progress = function(config){
22090     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22091 };
22092
22093 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22094     
22095     striped : false,
22096     active: false,
22097     
22098     getAutoCreate : function(){
22099         var cfg = {
22100             tag: 'div',
22101             cls: 'progress'
22102         };
22103         
22104         
22105         if(this.striped){
22106             cfg.cls += ' progress-striped';
22107         }
22108       
22109         if(this.active){
22110             cfg.cls += ' active';
22111         }
22112         
22113         
22114         return cfg;
22115     }
22116    
22117 });
22118
22119  
22120
22121  /*
22122  * - LGPL
22123  *
22124  * ProgressBar
22125  * 
22126  */
22127
22128 /**
22129  * @class Roo.bootstrap.ProgressBar
22130  * @extends Roo.bootstrap.Component
22131  * Bootstrap ProgressBar class
22132  * @cfg {Number} aria_valuenow aria-value now
22133  * @cfg {Number} aria_valuemin aria-value min
22134  * @cfg {Number} aria_valuemax aria-value max
22135  * @cfg {String} label label for the progress bar
22136  * @cfg {String} panel (success | info | warning | danger )
22137  * @cfg {String} role role of the progress bar
22138  * @cfg {String} sr_only text
22139  * 
22140  * 
22141  * @constructor
22142  * Create a new ProgressBar
22143  * @param {Object} config The config object
22144  */
22145
22146 Roo.bootstrap.ProgressBar = function(config){
22147     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22148 };
22149
22150 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22151     
22152     aria_valuenow : 0,
22153     aria_valuemin : 0,
22154     aria_valuemax : 100,
22155     label : false,
22156     panel : false,
22157     role : false,
22158     sr_only: false,
22159     
22160     getAutoCreate : function()
22161     {
22162         
22163         var cfg = {
22164             tag: 'div',
22165             cls: 'progress-bar',
22166             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22167         };
22168         
22169         if(this.sr_only){
22170             cfg.cn = {
22171                 tag: 'span',
22172                 cls: 'sr-only',
22173                 html: this.sr_only
22174             }
22175         }
22176         
22177         if(this.role){
22178             cfg.role = this.role;
22179         }
22180         
22181         if(this.aria_valuenow){
22182             cfg['aria-valuenow'] = this.aria_valuenow;
22183         }
22184         
22185         if(this.aria_valuemin){
22186             cfg['aria-valuemin'] = this.aria_valuemin;
22187         }
22188         
22189         if(this.aria_valuemax){
22190             cfg['aria-valuemax'] = this.aria_valuemax;
22191         }
22192         
22193         if(this.label && !this.sr_only){
22194             cfg.html = this.label;
22195         }
22196         
22197         if(this.panel){
22198             cfg.cls += ' progress-bar-' + this.panel;
22199         }
22200         
22201         return cfg;
22202     },
22203     
22204     update : function(aria_valuenow)
22205     {
22206         this.aria_valuenow = aria_valuenow;
22207         
22208         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22209     }
22210    
22211 });
22212
22213  
22214
22215  /**
22216  * @class Roo.bootstrap.TabGroup
22217  * @extends Roo.bootstrap.Column
22218  * @children Roo.bootstrap.TabPanel
22219  * Bootstrap Column class
22220  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22221  * @cfg {Boolean} carousel true to make the group behave like a carousel
22222  * @cfg {Boolean} bullets show bullets for the panels
22223  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22224  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22225  * @cfg {Boolean} showarrow (true|false) show arrow default true
22226  * 
22227  * @constructor
22228  * Create a new TabGroup
22229  * @param {Object} config The config object
22230  */
22231
22232 Roo.bootstrap.TabGroup = function(config){
22233     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22234     if (!this.navId) {
22235         this.navId = Roo.id();
22236     }
22237     this.tabs = [];
22238     Roo.bootstrap.TabGroup.register(this);
22239     
22240 };
22241
22242 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22243     
22244     carousel : false,
22245     transition : false,
22246     bullets : 0,
22247     timer : 0,
22248     autoslide : false,
22249     slideFn : false,
22250     slideOnTouch : false,
22251     showarrow : true,
22252     
22253     getAutoCreate : function()
22254     {
22255         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22256         
22257         cfg.cls += ' tab-content';
22258         
22259         if (this.carousel) {
22260             cfg.cls += ' carousel slide';
22261             
22262             cfg.cn = [{
22263                cls : 'carousel-inner',
22264                cn : []
22265             }];
22266         
22267             if(this.bullets  && !Roo.isTouch){
22268                 
22269                 var bullets = {
22270                     cls : 'carousel-bullets',
22271                     cn : []
22272                 };
22273                
22274                 if(this.bullets_cls){
22275                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22276                 }
22277                 
22278                 bullets.cn.push({
22279                     cls : 'clear'
22280                 });
22281                 
22282                 cfg.cn[0].cn.push(bullets);
22283             }
22284             
22285             if(this.showarrow){
22286                 cfg.cn[0].cn.push({
22287                     tag : 'div',
22288                     class : 'carousel-arrow',
22289                     cn : [
22290                         {
22291                             tag : 'div',
22292                             class : 'carousel-prev',
22293                             cn : [
22294                                 {
22295                                     tag : 'i',
22296                                     class : 'fa fa-chevron-left'
22297                                 }
22298                             ]
22299                         },
22300                         {
22301                             tag : 'div',
22302                             class : 'carousel-next',
22303                             cn : [
22304                                 {
22305                                     tag : 'i',
22306                                     class : 'fa fa-chevron-right'
22307                                 }
22308                             ]
22309                         }
22310                     ]
22311                 });
22312             }
22313             
22314         }
22315         
22316         return cfg;
22317     },
22318     
22319     initEvents:  function()
22320     {
22321 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22322 //            this.el.on("touchstart", this.onTouchStart, this);
22323 //        }
22324         
22325         if(this.autoslide){
22326             var _this = this;
22327             
22328             this.slideFn = window.setInterval(function() {
22329                 _this.showPanelNext();
22330             }, this.timer);
22331         }
22332         
22333         if(this.showarrow){
22334             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22335             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22336         }
22337         
22338         
22339     },
22340     
22341 //    onTouchStart : function(e, el, o)
22342 //    {
22343 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22344 //            return;
22345 //        }
22346 //        
22347 //        this.showPanelNext();
22348 //    },
22349     
22350     
22351     getChildContainer : function()
22352     {
22353         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22354     },
22355     
22356     /**
22357     * register a Navigation item
22358     * @param {Roo.bootstrap.nav.Item} the navitem to add
22359     */
22360     register : function(item)
22361     {
22362         this.tabs.push( item);
22363         item.navId = this.navId; // not really needed..
22364         this.addBullet();
22365     
22366     },
22367     
22368     getActivePanel : function()
22369     {
22370         var r = false;
22371         Roo.each(this.tabs, function(t) {
22372             if (t.active) {
22373                 r = t;
22374                 return false;
22375             }
22376             return null;
22377         });
22378         return r;
22379         
22380     },
22381     getPanelByName : function(n)
22382     {
22383         var r = false;
22384         Roo.each(this.tabs, function(t) {
22385             if (t.tabId == n) {
22386                 r = t;
22387                 return false;
22388             }
22389             return null;
22390         });
22391         return r;
22392     },
22393     indexOfPanel : function(p)
22394     {
22395         var r = false;
22396         Roo.each(this.tabs, function(t,i) {
22397             if (t.tabId == p.tabId) {
22398                 r = i;
22399                 return false;
22400             }
22401             return null;
22402         });
22403         return r;
22404     },
22405     /**
22406      * show a specific panel
22407      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22408      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22409      */
22410     showPanel : function (pan)
22411     {
22412         if(this.transition || typeof(pan) == 'undefined'){
22413             Roo.log("waiting for the transitionend");
22414             return false;
22415         }
22416         
22417         if (typeof(pan) == 'number') {
22418             pan = this.tabs[pan];
22419         }
22420         
22421         if (typeof(pan) == 'string') {
22422             pan = this.getPanelByName(pan);
22423         }
22424         
22425         var cur = this.getActivePanel();
22426         
22427         if(!pan || !cur){
22428             Roo.log('pan or acitve pan is undefined');
22429             return false;
22430         }
22431         
22432         if (pan.tabId == this.getActivePanel().tabId) {
22433             return true;
22434         }
22435         
22436         if (false === cur.fireEvent('beforedeactivate')) {
22437             return false;
22438         }
22439         
22440         if(this.bullets > 0 && !Roo.isTouch){
22441             this.setActiveBullet(this.indexOfPanel(pan));
22442         }
22443         
22444         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22445             
22446             //class="carousel-item carousel-item-next carousel-item-left"
22447             
22448             this.transition = true;
22449             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22450             var lr = dir == 'next' ? 'left' : 'right';
22451             pan.el.addClass(dir); // or prev
22452             pan.el.addClass('carousel-item-' + dir); // or prev
22453             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22454             cur.el.addClass(lr); // or right
22455             pan.el.addClass(lr);
22456             cur.el.addClass('carousel-item-' +lr); // or right
22457             pan.el.addClass('carousel-item-' +lr);
22458             
22459             
22460             var _this = this;
22461             cur.el.on('transitionend', function() {
22462                 Roo.log("trans end?");
22463                 
22464                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22465                 pan.setActive(true);
22466                 
22467                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22468                 cur.setActive(false);
22469                 
22470                 _this.transition = false;
22471                 
22472             }, this, { single:  true } );
22473             
22474             return true;
22475         }
22476         
22477         cur.setActive(false);
22478         pan.setActive(true);
22479         
22480         return true;
22481         
22482     },
22483     showPanelNext : function()
22484     {
22485         var i = this.indexOfPanel(this.getActivePanel());
22486         
22487         if (i >= this.tabs.length - 1 && !this.autoslide) {
22488             return;
22489         }
22490         
22491         if (i >= this.tabs.length - 1 && this.autoslide) {
22492             i = -1;
22493         }
22494         
22495         this.showPanel(this.tabs[i+1]);
22496     },
22497     
22498     showPanelPrev : function()
22499     {
22500         var i = this.indexOfPanel(this.getActivePanel());
22501         
22502         if (i  < 1 && !this.autoslide) {
22503             return;
22504         }
22505         
22506         if (i < 1 && this.autoslide) {
22507             i = this.tabs.length;
22508         }
22509         
22510         this.showPanel(this.tabs[i-1]);
22511     },
22512     
22513     
22514     addBullet: function()
22515     {
22516         if(!this.bullets || Roo.isTouch){
22517             return;
22518         }
22519         var ctr = this.el.select('.carousel-bullets',true).first();
22520         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22521         var bullet = ctr.createChild({
22522             cls : 'bullet bullet-' + i
22523         },ctr.dom.lastChild);
22524         
22525         
22526         var _this = this;
22527         
22528         bullet.on('click', (function(e, el, o, ii, t){
22529
22530             e.preventDefault();
22531
22532             this.showPanel(ii);
22533
22534             if(this.autoslide && this.slideFn){
22535                 clearInterval(this.slideFn);
22536                 this.slideFn = window.setInterval(function() {
22537                     _this.showPanelNext();
22538                 }, this.timer);
22539             }
22540
22541         }).createDelegate(this, [i, bullet], true));
22542                 
22543         
22544     },
22545      
22546     setActiveBullet : function(i)
22547     {
22548         if(Roo.isTouch){
22549             return;
22550         }
22551         
22552         Roo.each(this.el.select('.bullet', true).elements, function(el){
22553             el.removeClass('selected');
22554         });
22555
22556         var bullet = this.el.select('.bullet-' + i, true).first();
22557         
22558         if(!bullet){
22559             return;
22560         }
22561         
22562         bullet.addClass('selected');
22563     }
22564     
22565     
22566   
22567 });
22568
22569  
22570
22571  
22572  
22573 Roo.apply(Roo.bootstrap.TabGroup, {
22574     
22575     groups: {},
22576      /**
22577     * register a Navigation Group
22578     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22579     */
22580     register : function(navgrp)
22581     {
22582         this.groups[navgrp.navId] = navgrp;
22583         
22584     },
22585     /**
22586     * fetch a Navigation Group based on the navigation ID
22587     * if one does not exist , it will get created.
22588     * @param {string} the navgroup to add
22589     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22590     */
22591     get: function(navId) {
22592         if (typeof(this.groups[navId]) == 'undefined') {
22593             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22594         }
22595         return this.groups[navId] ;
22596     }
22597     
22598     
22599     
22600 });
22601
22602  /*
22603  * - LGPL
22604  *
22605  * TabPanel
22606  * 
22607  */
22608
22609 /**
22610  * @class Roo.bootstrap.TabPanel
22611  * @extends Roo.bootstrap.Component
22612  * @children Roo.bootstrap.Component
22613  * Bootstrap TabPanel class
22614  * @cfg {Boolean} active panel active
22615  * @cfg {String} html panel content
22616  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22617  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22618  * @cfg {String} href click to link..
22619  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22620  * 
22621  * 
22622  * @constructor
22623  * Create a new TabPanel
22624  * @param {Object} config The config object
22625  */
22626
22627 Roo.bootstrap.TabPanel = function(config){
22628     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22629     this.addEvents({
22630         /**
22631              * @event changed
22632              * Fires when the active status changes
22633              * @param {Roo.bootstrap.TabPanel} this
22634              * @param {Boolean} state the new state
22635             
22636          */
22637         'changed': true,
22638         /**
22639              * @event beforedeactivate
22640              * Fires before a tab is de-activated - can be used to do validation on a form.
22641              * @param {Roo.bootstrap.TabPanel} this
22642              * @return {Boolean} false if there is an error
22643             
22644          */
22645         'beforedeactivate': true
22646      });
22647     
22648     this.tabId = this.tabId || Roo.id();
22649   
22650 };
22651
22652 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22653     
22654     active: false,
22655     html: false,
22656     tabId: false,
22657     navId : false,
22658     href : '',
22659     touchSlide : false,
22660     getAutoCreate : function(){
22661         
22662         
22663         var cfg = {
22664             tag: 'div',
22665             // item is needed for carousel - not sure if it has any effect otherwise
22666             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22667             html: this.html || ''
22668         };
22669         
22670         if(this.active){
22671             cfg.cls += ' active';
22672         }
22673         
22674         if(this.tabId){
22675             cfg.tabId = this.tabId;
22676         }
22677         
22678         
22679         
22680         return cfg;
22681     },
22682     
22683     initEvents:  function()
22684     {
22685         var p = this.parent();
22686         
22687         this.navId = this.navId || p.navId;
22688         
22689         if (typeof(this.navId) != 'undefined') {
22690             // not really needed.. but just in case.. parent should be a NavGroup.
22691             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22692             
22693             tg.register(this);
22694             
22695             var i = tg.tabs.length - 1;
22696             
22697             if(this.active && tg.bullets > 0 && i < tg.bullets){
22698                 tg.setActiveBullet(i);
22699             }
22700         }
22701         
22702         this.el.on('click', this.onClick, this);
22703         
22704         if(Roo.isTouch && this.touchSlide){
22705             this.el.on("touchstart", this.onTouchStart, this);
22706             this.el.on("touchmove", this.onTouchMove, this);
22707             this.el.on("touchend", this.onTouchEnd, this);
22708         }
22709         
22710     },
22711     
22712     onRender : function(ct, position)
22713     {
22714         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22715     },
22716     
22717     setActive : function(state)
22718     {
22719         Roo.log("panel - set active " + this.tabId + "=" + state);
22720         
22721         this.active = state;
22722         if (!state) {
22723             this.el.removeClass('active');
22724             
22725         } else  if (!this.el.hasClass('active')) {
22726             this.el.addClass('active');
22727         }
22728         
22729         this.fireEvent('changed', this, state);
22730     },
22731     
22732     onClick : function(e)
22733     {
22734         e.preventDefault();
22735         
22736         if(!this.href.length){
22737             return;
22738         }
22739         
22740         window.location.href = this.href;
22741     },
22742     
22743     startX : 0,
22744     startY : 0,
22745     endX : 0,
22746     endY : 0,
22747     swiping : false,
22748     
22749     onTouchStart : function(e)
22750     {
22751         this.swiping = false;
22752         
22753         this.startX = e.browserEvent.touches[0].clientX;
22754         this.startY = e.browserEvent.touches[0].clientY;
22755     },
22756     
22757     onTouchMove : function(e)
22758     {
22759         this.swiping = true;
22760         
22761         this.endX = e.browserEvent.touches[0].clientX;
22762         this.endY = e.browserEvent.touches[0].clientY;
22763     },
22764     
22765     onTouchEnd : function(e)
22766     {
22767         if(!this.swiping){
22768             this.onClick(e);
22769             return;
22770         }
22771         
22772         var tabGroup = this.parent();
22773         
22774         if(this.endX > this.startX){ // swiping right
22775             tabGroup.showPanelPrev();
22776             return;
22777         }
22778         
22779         if(this.startX > this.endX){ // swiping left
22780             tabGroup.showPanelNext();
22781             return;
22782         }
22783     }
22784     
22785     
22786 });
22787  
22788
22789  
22790
22791  /*
22792  * - LGPL
22793  *
22794  * DateField
22795  * 
22796  */
22797
22798 /**
22799  * @class Roo.bootstrap.form.DateField
22800  * @extends Roo.bootstrap.form.Input
22801  * Bootstrap DateField class
22802  * @cfg {Number} weekStart default 0
22803  * @cfg {String} viewMode default empty, (months|years)
22804  * @cfg {String} minViewMode default empty, (months|years)
22805  * @cfg {Number} startDate default -Infinity
22806  * @cfg {Number} endDate default Infinity
22807  * @cfg {Boolean} todayHighlight default false
22808  * @cfg {Boolean} todayBtn default false
22809  * @cfg {Boolean} calendarWeeks default false
22810  * @cfg {Object} daysOfWeekDisabled default empty
22811  * @cfg {Boolean} singleMode default false (true | false)
22812  * 
22813  * @cfg {Boolean} keyboardNavigation default true
22814  * @cfg {String} language default en
22815  * 
22816  * @constructor
22817  * Create a new DateField
22818  * @param {Object} config The config object
22819  */
22820
22821 Roo.bootstrap.form.DateField = function(config){
22822     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22823      this.addEvents({
22824             /**
22825              * @event show
22826              * Fires when this field show.
22827              * @param {Roo.bootstrap.form.DateField} this
22828              * @param {Mixed} date The date value
22829              */
22830             show : true,
22831             /**
22832              * @event show
22833              * Fires when this field hide.
22834              * @param {Roo.bootstrap.form.DateField} this
22835              * @param {Mixed} date The date value
22836              */
22837             hide : true,
22838             /**
22839              * @event select
22840              * Fires when select a date.
22841              * @param {Roo.bootstrap.form.DateField} this
22842              * @param {Mixed} date The date value
22843              */
22844             select : true,
22845             /**
22846              * @event beforeselect
22847              * Fires when before select a date.
22848              * @param {Roo.bootstrap.form.DateField} this
22849              * @param {Mixed} date The date value
22850              */
22851             beforeselect : true
22852         });
22853 };
22854
22855 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22856     
22857     /**
22858      * @cfg {String} format
22859      * The default date format string which can be overriden for localization support.  The format must be
22860      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22861      */
22862     format : "m/d/y",
22863     /**
22864      * @cfg {String} altFormats
22865      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22866      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22867      */
22868     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22869     
22870     weekStart : 0,
22871     
22872     viewMode : '',
22873     
22874     minViewMode : '',
22875     
22876     todayHighlight : false,
22877     
22878     todayBtn: false,
22879     
22880     language: 'en',
22881     
22882     keyboardNavigation: true,
22883     
22884     calendarWeeks: false,
22885     
22886     startDate: -Infinity,
22887     
22888     endDate: Infinity,
22889     
22890     daysOfWeekDisabled: [],
22891     
22892     _events: [],
22893     
22894     singleMode : false,
22895     
22896     UTCDate: function()
22897     {
22898         return new Date(Date.UTC.apply(Date, arguments));
22899     },
22900     
22901     UTCToday: function()
22902     {
22903         var today = new Date();
22904         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22905     },
22906     
22907     getDate: function() {
22908             var d = this.getUTCDate();
22909             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22910     },
22911     
22912     getUTCDate: function() {
22913             return this.date;
22914     },
22915     
22916     setDate: function(d) {
22917             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22918     },
22919     
22920     setUTCDate: function(d) {
22921             this.date = d;
22922             this.setValue(this.formatDate(this.date));
22923     },
22924         
22925     onRender: function(ct, position)
22926     {
22927         
22928         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22929         
22930         this.language = this.language || 'en';
22931         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22932         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22933         
22934         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22935         this.format = this.format || 'm/d/y';
22936         this.isInline = false;
22937         this.isInput = true;
22938         this.component = this.el.select('.add-on', true).first() || false;
22939         this.component = (this.component && this.component.length === 0) ? false : this.component;
22940         this.hasInput = this.component && this.inputEl().length;
22941         
22942         if (typeof(this.minViewMode === 'string')) {
22943             switch (this.minViewMode) {
22944                 case 'months':
22945                     this.minViewMode = 1;
22946                     break;
22947                 case 'years':
22948                     this.minViewMode = 2;
22949                     break;
22950                 default:
22951                     this.minViewMode = 0;
22952                     break;
22953             }
22954         }
22955         
22956         if (typeof(this.viewMode === 'string')) {
22957             switch (this.viewMode) {
22958                 case 'months':
22959                     this.viewMode = 1;
22960                     break;
22961                 case 'years':
22962                     this.viewMode = 2;
22963                     break;
22964                 default:
22965                     this.viewMode = 0;
22966                     break;
22967             }
22968         }
22969                 
22970         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22971         
22972 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22973         
22974         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22975         
22976         this.picker().on('mousedown', this.onMousedown, this);
22977         this.picker().on('click', this.onClick, this);
22978         
22979         this.picker().addClass('datepicker-dropdown');
22980         
22981         this.startViewMode = this.viewMode;
22982         
22983         if(this.singleMode){
22984             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22985                 v.setVisibilityMode(Roo.Element.DISPLAY);
22986                 v.hide();
22987             });
22988             
22989             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22990                 v.setStyle('width', '189px');
22991             });
22992         }
22993         
22994         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22995             if(!this.calendarWeeks){
22996                 v.remove();
22997                 return;
22998             }
22999             
23000             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23001             v.attr('colspan', function(i, val){
23002                 return parseInt(val) + 1;
23003             });
23004         });
23005                         
23006         
23007         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23008         
23009         this.setStartDate(this.startDate);
23010         this.setEndDate(this.endDate);
23011         
23012         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23013         
23014         this.fillDow();
23015         this.fillMonths();
23016         this.update();
23017         this.showMode();
23018         
23019         if(this.isInline) {
23020             this.showPopup();
23021         }
23022     },
23023     
23024     picker : function()
23025     {
23026         return this.pickerEl;
23027 //        return this.el.select('.datepicker', true).first();
23028     },
23029     
23030     fillDow: function()
23031     {
23032         var dowCnt = this.weekStart;
23033         
23034         var dow = {
23035             tag: 'tr',
23036             cn: [
23037                 
23038             ]
23039         };
23040         
23041         if(this.calendarWeeks){
23042             dow.cn.push({
23043                 tag: 'th',
23044                 cls: 'cw',
23045                 html: '&nbsp;'
23046             })
23047         }
23048         
23049         while (dowCnt < this.weekStart + 7) {
23050             dow.cn.push({
23051                 tag: 'th',
23052                 cls: 'dow',
23053                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23054             });
23055         }
23056         
23057         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23058     },
23059     
23060     fillMonths: function()
23061     {    
23062         var i = 0;
23063         var months = this.picker().select('>.datepicker-months td', true).first();
23064         
23065         months.dom.innerHTML = '';
23066         
23067         while (i < 12) {
23068             var month = {
23069                 tag: 'span',
23070                 cls: 'month',
23071                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23072             };
23073             
23074             months.createChild(month);
23075         }
23076         
23077     },
23078     
23079     update: function()
23080     {
23081         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;
23082         
23083         if (this.date < this.startDate) {
23084             this.viewDate = new Date(this.startDate);
23085         } else if (this.date > this.endDate) {
23086             this.viewDate = new Date(this.endDate);
23087         } else {
23088             this.viewDate = new Date(this.date);
23089         }
23090         
23091         this.fill();
23092     },
23093     
23094     fill: function() 
23095     {
23096         var d = new Date(this.viewDate),
23097                 year = d.getUTCFullYear(),
23098                 month = d.getUTCMonth(),
23099                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23100                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23101                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23102                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23103                 currentDate = this.date && this.date.valueOf(),
23104                 today = this.UTCToday();
23105         
23106         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23107         
23108 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23109         
23110 //        this.picker.select('>tfoot th.today').
23111 //                                              .text(dates[this.language].today)
23112 //                                              .toggle(this.todayBtn !== false);
23113     
23114         this.updateNavArrows();
23115         this.fillMonths();
23116                                                 
23117         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23118         
23119         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23120          
23121         prevMonth.setUTCDate(day);
23122         
23123         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23124         
23125         var nextMonth = new Date(prevMonth);
23126         
23127         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23128         
23129         nextMonth = nextMonth.valueOf();
23130         
23131         var fillMonths = false;
23132         
23133         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23134         
23135         while(prevMonth.valueOf() <= nextMonth) {
23136             var clsName = '';
23137             
23138             if (prevMonth.getUTCDay() === this.weekStart) {
23139                 if(fillMonths){
23140                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23141                 }
23142                     
23143                 fillMonths = {
23144                     tag: 'tr',
23145                     cn: []
23146                 };
23147                 
23148                 if(this.calendarWeeks){
23149                     // ISO 8601: First week contains first thursday.
23150                     // ISO also states week starts on Monday, but we can be more abstract here.
23151                     var
23152                     // Start of current week: based on weekstart/current date
23153                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23154                     // Thursday of this week
23155                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23156                     // First Thursday of year, year from thursday
23157                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23158                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23159                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23160                     
23161                     fillMonths.cn.push({
23162                         tag: 'td',
23163                         cls: 'cw',
23164                         html: calWeek
23165                     });
23166                 }
23167             }
23168             
23169             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23170                 clsName += ' old';
23171             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23172                 clsName += ' new';
23173             }
23174             if (this.todayHighlight &&
23175                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23176                 prevMonth.getUTCMonth() == today.getMonth() &&
23177                 prevMonth.getUTCDate() == today.getDate()) {
23178                 clsName += ' today';
23179             }
23180             
23181             if (currentDate && prevMonth.valueOf() === currentDate) {
23182                 clsName += ' active';
23183             }
23184             
23185             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23186                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23187                     clsName += ' disabled';
23188             }
23189             
23190             fillMonths.cn.push({
23191                 tag: 'td',
23192                 cls: 'day ' + clsName,
23193                 html: prevMonth.getDate()
23194             });
23195             
23196             prevMonth.setDate(prevMonth.getDate()+1);
23197         }
23198           
23199         var currentYear = this.date && this.date.getUTCFullYear();
23200         var currentMonth = this.date && this.date.getUTCMonth();
23201         
23202         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23203         
23204         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23205             v.removeClass('active');
23206             
23207             if(currentYear === year && k === currentMonth){
23208                 v.addClass('active');
23209             }
23210             
23211             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23212                 v.addClass('disabled');
23213             }
23214             
23215         });
23216         
23217         
23218         year = parseInt(year/10, 10) * 10;
23219         
23220         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23221         
23222         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23223         
23224         year -= 1;
23225         for (var i = -1; i < 11; i++) {
23226             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23227                 tag: 'span',
23228                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23229                 html: year
23230             });
23231             
23232             year += 1;
23233         }
23234     },
23235     
23236     showMode: function(dir) 
23237     {
23238         if (dir) {
23239             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23240         }
23241         
23242         Roo.each(this.picker().select('>div',true).elements, function(v){
23243             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23244             v.hide();
23245         });
23246         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23247     },
23248     
23249     place: function()
23250     {
23251         if(this.isInline) {
23252             return;
23253         }
23254         
23255         this.picker().removeClass(['bottom', 'top']);
23256         
23257         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23258             /*
23259              * place to the top of element!
23260              *
23261              */
23262             
23263             this.picker().addClass('top');
23264             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23265             
23266             return;
23267         }
23268         
23269         this.picker().addClass('bottom');
23270         
23271         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23272     },
23273     
23274     parseDate : function(value)
23275     {
23276         if(!value || value instanceof Date){
23277             return value;
23278         }
23279         var v = Date.parseDate(value, this.format);
23280         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23281             v = Date.parseDate(value, 'Y-m-d');
23282         }
23283         if(!v && this.altFormats){
23284             if(!this.altFormatsArray){
23285                 this.altFormatsArray = this.altFormats.split("|");
23286             }
23287             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23288                 v = Date.parseDate(value, this.altFormatsArray[i]);
23289             }
23290         }
23291         return v;
23292     },
23293     
23294     formatDate : function(date, fmt)
23295     {   
23296         return (!date || !(date instanceof Date)) ?
23297         date : date.dateFormat(fmt || this.format);
23298     },
23299     
23300     onFocus : function()
23301     {
23302         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23303         this.showPopup();
23304     },
23305     
23306     onBlur : function()
23307     {
23308         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23309         
23310         var d = this.inputEl().getValue();
23311         
23312         this.setValue(d);
23313                 
23314         this.hidePopup();
23315     },
23316     
23317     showPopup : function()
23318     {
23319         this.picker().show();
23320         this.update();
23321         this.place();
23322         
23323         this.fireEvent('showpopup', this, this.date);
23324     },
23325     
23326     hidePopup : function()
23327     {
23328         if(this.isInline) {
23329             return;
23330         }
23331         this.picker().hide();
23332         this.viewMode = this.startViewMode;
23333         this.showMode();
23334         
23335         this.fireEvent('hidepopup', this, this.date);
23336         
23337     },
23338     
23339     onMousedown: function(e)
23340     {
23341         e.stopPropagation();
23342         e.preventDefault();
23343     },
23344     
23345     keyup: function(e)
23346     {
23347         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23348         this.update();
23349     },
23350
23351     setValue: function(v)
23352     {
23353         if(this.fireEvent('beforeselect', this, v) !== false){
23354             var d = new Date(this.parseDate(v) ).clearTime();
23355         
23356             if(isNaN(d.getTime())){
23357                 this.date = this.viewDate = '';
23358                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23359                 return;
23360             }
23361
23362             v = this.formatDate(d);
23363
23364             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23365
23366             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23367
23368             this.update();
23369
23370             this.fireEvent('select', this, this.date);
23371         }
23372     },
23373     
23374     getValue: function()
23375     {
23376         return this.formatDate(this.date);
23377     },
23378     
23379     fireKey: function(e)
23380     {
23381         if (!this.picker().isVisible()){
23382             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23383                 this.showPopup();
23384             }
23385             return;
23386         }
23387         
23388         var dateChanged = false,
23389         dir, day, month,
23390         newDate, newViewDate;
23391         
23392         switch(e.keyCode){
23393             case 27: // escape
23394                 this.hidePopup();
23395                 e.preventDefault();
23396                 break;
23397             case 37: // left
23398             case 39: // right
23399                 if (!this.keyboardNavigation) {
23400                     break;
23401                 }
23402                 dir = e.keyCode == 37 ? -1 : 1;
23403                 
23404                 if (e.ctrlKey){
23405                     newDate = this.moveYear(this.date, dir);
23406                     newViewDate = this.moveYear(this.viewDate, dir);
23407                 } else if (e.shiftKey){
23408                     newDate = this.moveMonth(this.date, dir);
23409                     newViewDate = this.moveMonth(this.viewDate, dir);
23410                 } else {
23411                     newDate = new Date(this.date);
23412                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23413                     newViewDate = new Date(this.viewDate);
23414                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23415                 }
23416                 if (this.dateWithinRange(newDate)){
23417                     this.date = newDate;
23418                     this.viewDate = newViewDate;
23419                     this.setValue(this.formatDate(this.date));
23420 //                    this.update();
23421                     e.preventDefault();
23422                     dateChanged = true;
23423                 }
23424                 break;
23425             case 38: // up
23426             case 40: // down
23427                 if (!this.keyboardNavigation) {
23428                     break;
23429                 }
23430                 dir = e.keyCode == 38 ? -1 : 1;
23431                 if (e.ctrlKey){
23432                     newDate = this.moveYear(this.date, dir);
23433                     newViewDate = this.moveYear(this.viewDate, dir);
23434                 } else if (e.shiftKey){
23435                     newDate = this.moveMonth(this.date, dir);
23436                     newViewDate = this.moveMonth(this.viewDate, dir);
23437                 } else {
23438                     newDate = new Date(this.date);
23439                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23440                     newViewDate = new Date(this.viewDate);
23441                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23442                 }
23443                 if (this.dateWithinRange(newDate)){
23444                     this.date = newDate;
23445                     this.viewDate = newViewDate;
23446                     this.setValue(this.formatDate(this.date));
23447 //                    this.update();
23448                     e.preventDefault();
23449                     dateChanged = true;
23450                 }
23451                 break;
23452             case 13: // enter
23453                 this.setValue(this.formatDate(this.date));
23454                 this.hidePopup();
23455                 e.preventDefault();
23456                 break;
23457             case 9: // tab
23458                 this.setValue(this.formatDate(this.date));
23459                 this.hidePopup();
23460                 break;
23461             case 16: // shift
23462             case 17: // ctrl
23463             case 18: // alt
23464                 break;
23465             default :
23466                 this.hidePopup();
23467                 
23468         }
23469     },
23470     
23471     
23472     onClick: function(e) 
23473     {
23474         e.stopPropagation();
23475         e.preventDefault();
23476         
23477         var target = e.getTarget();
23478         
23479         if(target.nodeName.toLowerCase() === 'i'){
23480             target = Roo.get(target).dom.parentNode;
23481         }
23482         
23483         var nodeName = target.nodeName;
23484         var className = target.className;
23485         var html = target.innerHTML;
23486         //Roo.log(nodeName);
23487         
23488         switch(nodeName.toLowerCase()) {
23489             case 'th':
23490                 switch(className) {
23491                     case 'switch':
23492                         this.showMode(1);
23493                         break;
23494                     case 'prev':
23495                     case 'next':
23496                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23497                         switch(this.viewMode){
23498                                 case 0:
23499                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23500                                         break;
23501                                 case 1:
23502                                 case 2:
23503                                         this.viewDate = this.moveYear(this.viewDate, dir);
23504                                         break;
23505                         }
23506                         this.fill();
23507                         break;
23508                     case 'today':
23509                         var date = new Date();
23510                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23511 //                        this.fill()
23512                         this.setValue(this.formatDate(this.date));
23513                         
23514                         this.hidePopup();
23515                         break;
23516                 }
23517                 break;
23518             case 'span':
23519                 if (className.indexOf('disabled') < 0) {
23520                 if (!this.viewDate) {
23521                     this.viewDate = new Date();
23522                 }
23523                 this.viewDate.setUTCDate(1);
23524                     if (className.indexOf('month') > -1) {
23525                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23526                     } else {
23527                         var year = parseInt(html, 10) || 0;
23528                         this.viewDate.setUTCFullYear(year);
23529                         
23530                     }
23531                     
23532                     if(this.singleMode){
23533                         this.setValue(this.formatDate(this.viewDate));
23534                         this.hidePopup();
23535                         return;
23536                     }
23537                     
23538                     this.showMode(-1);
23539                     this.fill();
23540                 }
23541                 break;
23542                 
23543             case 'td':
23544                 //Roo.log(className);
23545                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23546                     var day = parseInt(html, 10) || 1;
23547                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23548                         month = (this.viewDate || new Date()).getUTCMonth();
23549
23550                     if (className.indexOf('old') > -1) {
23551                         if(month === 0 ){
23552                             month = 11;
23553                             year -= 1;
23554                         }else{
23555                             month -= 1;
23556                         }
23557                     } else if (className.indexOf('new') > -1) {
23558                         if (month == 11) {
23559                             month = 0;
23560                             year += 1;
23561                         } else {
23562                             month += 1;
23563                         }
23564                     }
23565                     //Roo.log([year,month,day]);
23566                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23567                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23568 //                    this.fill();
23569                     //Roo.log(this.formatDate(this.date));
23570                     this.setValue(this.formatDate(this.date));
23571                     this.hidePopup();
23572                 }
23573                 break;
23574         }
23575     },
23576     
23577     setStartDate: function(startDate)
23578     {
23579         this.startDate = startDate || -Infinity;
23580         if (this.startDate !== -Infinity) {
23581             this.startDate = this.parseDate(this.startDate);
23582         }
23583         this.update();
23584         this.updateNavArrows();
23585     },
23586
23587     setEndDate: function(endDate)
23588     {
23589         this.endDate = endDate || Infinity;
23590         if (this.endDate !== Infinity) {
23591             this.endDate = this.parseDate(this.endDate);
23592         }
23593         this.update();
23594         this.updateNavArrows();
23595     },
23596     
23597     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23598     {
23599         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23600         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23601             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23602         }
23603         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23604             return parseInt(d, 10);
23605         });
23606         this.update();
23607         this.updateNavArrows();
23608     },
23609     
23610     updateNavArrows: function() 
23611     {
23612         if(this.singleMode){
23613             return;
23614         }
23615         
23616         var d = new Date(this.viewDate),
23617         year = d.getUTCFullYear(),
23618         month = d.getUTCMonth();
23619         
23620         Roo.each(this.picker().select('.prev', true).elements, function(v){
23621             v.show();
23622             switch (this.viewMode) {
23623                 case 0:
23624
23625                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23626                         v.hide();
23627                     }
23628                     break;
23629                 case 1:
23630                 case 2:
23631                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23632                         v.hide();
23633                     }
23634                     break;
23635             }
23636         });
23637         
23638         Roo.each(this.picker().select('.next', true).elements, function(v){
23639             v.show();
23640             switch (this.viewMode) {
23641                 case 0:
23642
23643                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23644                         v.hide();
23645                     }
23646                     break;
23647                 case 1:
23648                 case 2:
23649                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23650                         v.hide();
23651                     }
23652                     break;
23653             }
23654         })
23655     },
23656     
23657     moveMonth: function(date, dir)
23658     {
23659         if (!dir) {
23660             return date;
23661         }
23662         var new_date = new Date(date.valueOf()),
23663         day = new_date.getUTCDate(),
23664         month = new_date.getUTCMonth(),
23665         mag = Math.abs(dir),
23666         new_month, test;
23667         dir = dir > 0 ? 1 : -1;
23668         if (mag == 1){
23669             test = dir == -1
23670             // If going back one month, make sure month is not current month
23671             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23672             ? function(){
23673                 return new_date.getUTCMonth() == month;
23674             }
23675             // If going forward one month, make sure month is as expected
23676             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23677             : function(){
23678                 return new_date.getUTCMonth() != new_month;
23679             };
23680             new_month = month + dir;
23681             new_date.setUTCMonth(new_month);
23682             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23683             if (new_month < 0 || new_month > 11) {
23684                 new_month = (new_month + 12) % 12;
23685             }
23686         } else {
23687             // For magnitudes >1, move one month at a time...
23688             for (var i=0; i<mag; i++) {
23689                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23690                 new_date = this.moveMonth(new_date, dir);
23691             }
23692             // ...then reset the day, keeping it in the new month
23693             new_month = new_date.getUTCMonth();
23694             new_date.setUTCDate(day);
23695             test = function(){
23696                 return new_month != new_date.getUTCMonth();
23697             };
23698         }
23699         // Common date-resetting loop -- if date is beyond end of month, make it
23700         // end of month
23701         while (test()){
23702             new_date.setUTCDate(--day);
23703             new_date.setUTCMonth(new_month);
23704         }
23705         return new_date;
23706     },
23707
23708     moveYear: function(date, dir)
23709     {
23710         return this.moveMonth(date, dir*12);
23711     },
23712
23713     dateWithinRange: function(date)
23714     {
23715         return date >= this.startDate && date <= this.endDate;
23716     },
23717
23718     
23719     remove: function() 
23720     {
23721         this.picker().remove();
23722     },
23723     
23724     validateValue : function(value)
23725     {
23726         if(this.getVisibilityEl().hasClass('hidden')){
23727             return true;
23728         }
23729         
23730         if(value.length < 1)  {
23731             if(this.allowBlank){
23732                 return true;
23733             }
23734             return false;
23735         }
23736         
23737         if(value.length < this.minLength){
23738             return false;
23739         }
23740         if(value.length > this.maxLength){
23741             return false;
23742         }
23743         if(this.vtype){
23744             var vt = Roo.form.VTypes;
23745             if(!vt[this.vtype](value, this)){
23746                 return false;
23747             }
23748         }
23749         if(typeof this.validator == "function"){
23750             var msg = this.validator(value);
23751             if(msg !== true){
23752                 return false;
23753             }
23754         }
23755         
23756         if(this.regex && !this.regex.test(value)){
23757             return false;
23758         }
23759         
23760         if(typeof(this.parseDate(value)) == 'undefined'){
23761             return false;
23762         }
23763         
23764         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23765             return false;
23766         }      
23767         
23768         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23769             return false;
23770         } 
23771         
23772         
23773         return true;
23774     },
23775     
23776     reset : function()
23777     {
23778         this.date = this.viewDate = '';
23779         
23780         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23781     }
23782    
23783 });
23784
23785 Roo.apply(Roo.bootstrap.form.DateField,  {
23786     
23787     head : {
23788         tag: 'thead',
23789         cn: [
23790         {
23791             tag: 'tr',
23792             cn: [
23793             {
23794                 tag: 'th',
23795                 cls: 'prev',
23796                 html: '<i class="fa fa-arrow-left"/>'
23797             },
23798             {
23799                 tag: 'th',
23800                 cls: 'switch',
23801                 colspan: '5'
23802             },
23803             {
23804                 tag: 'th',
23805                 cls: 'next',
23806                 html: '<i class="fa fa-arrow-right"/>'
23807             }
23808
23809             ]
23810         }
23811         ]
23812     },
23813     
23814     content : {
23815         tag: 'tbody',
23816         cn: [
23817         {
23818             tag: 'tr',
23819             cn: [
23820             {
23821                 tag: 'td',
23822                 colspan: '7'
23823             }
23824             ]
23825         }
23826         ]
23827     },
23828     
23829     footer : {
23830         tag: 'tfoot',
23831         cn: [
23832         {
23833             tag: 'tr',
23834             cn: [
23835             {
23836                 tag: 'th',
23837                 colspan: '7',
23838                 cls: 'today'
23839             }
23840                     
23841             ]
23842         }
23843         ]
23844     },
23845     
23846     dates:{
23847         en: {
23848             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23849             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23850             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23851             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23852             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23853             today: "Today"
23854         }
23855     },
23856     
23857     modes: [
23858     {
23859         clsName: 'days',
23860         navFnc: 'Month',
23861         navStep: 1
23862     },
23863     {
23864         clsName: 'months',
23865         navFnc: 'FullYear',
23866         navStep: 1
23867     },
23868     {
23869         clsName: 'years',
23870         navFnc: 'FullYear',
23871         navStep: 10
23872     }]
23873 });
23874
23875 Roo.apply(Roo.bootstrap.form.DateField,  {
23876   
23877     template : {
23878         tag: 'div',
23879         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23880         cn: [
23881         {
23882             tag: 'div',
23883             cls: 'datepicker-days',
23884             cn: [
23885             {
23886                 tag: 'table',
23887                 cls: 'table-condensed',
23888                 cn:[
23889                 Roo.bootstrap.form.DateField.head,
23890                 {
23891                     tag: 'tbody'
23892                 },
23893                 Roo.bootstrap.form.DateField.footer
23894                 ]
23895             }
23896             ]
23897         },
23898         {
23899             tag: 'div',
23900             cls: 'datepicker-months',
23901             cn: [
23902             {
23903                 tag: 'table',
23904                 cls: 'table-condensed',
23905                 cn:[
23906                 Roo.bootstrap.form.DateField.head,
23907                 Roo.bootstrap.form.DateField.content,
23908                 Roo.bootstrap.form.DateField.footer
23909                 ]
23910             }
23911             ]
23912         },
23913         {
23914             tag: 'div',
23915             cls: 'datepicker-years',
23916             cn: [
23917             {
23918                 tag: 'table',
23919                 cls: 'table-condensed',
23920                 cn:[
23921                 Roo.bootstrap.form.DateField.head,
23922                 Roo.bootstrap.form.DateField.content,
23923                 Roo.bootstrap.form.DateField.footer
23924                 ]
23925             }
23926             ]
23927         }
23928         ]
23929     }
23930 });
23931
23932  
23933
23934  /*
23935  * - LGPL
23936  *
23937  * TimeField
23938  * 
23939  */
23940
23941 /**
23942  * @class Roo.bootstrap.form.TimeField
23943  * @extends Roo.bootstrap.form.Input
23944  * Bootstrap DateField class
23945  * 
23946  * 
23947  * @constructor
23948  * Create a new TimeField
23949  * @param {Object} config The config object
23950  */
23951
23952 Roo.bootstrap.form.TimeField = function(config){
23953     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23954     this.addEvents({
23955             /**
23956              * @event show
23957              * Fires when this field show.
23958              * @param {Roo.bootstrap.form.DateField} thisthis
23959              * @param {Mixed} date The date value
23960              */
23961             show : true,
23962             /**
23963              * @event show
23964              * Fires when this field hide.
23965              * @param {Roo.bootstrap.form.DateField} this
23966              * @param {Mixed} date The date value
23967              */
23968             hide : true,
23969             /**
23970              * @event select
23971              * Fires when select a date.
23972              * @param {Roo.bootstrap.form.DateField} this
23973              * @param {Mixed} date The date value
23974              */
23975             select : true
23976         });
23977 };
23978
23979 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23980     
23981     /**
23982      * @cfg {String} format
23983      * The default time format string which can be overriden for localization support.  The format must be
23984      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23985      */
23986     format : "H:i",
23987
23988     getAutoCreate : function()
23989     {
23990         this.after = '<i class="fa far fa-clock"></i>';
23991         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23992         
23993          
23994     },
23995     onRender: function(ct, position)
23996     {
23997         
23998         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23999                 
24000         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24001         
24002         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24003         
24004         this.pop = this.picker().select('>.datepicker-time',true).first();
24005         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24006         
24007         this.picker().on('mousedown', this.onMousedown, this);
24008         this.picker().on('click', this.onClick, this);
24009         
24010         this.picker().addClass('datepicker-dropdown');
24011     
24012         this.fillTime();
24013         this.update();
24014             
24015         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24016         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24017         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24018         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24019         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24020         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24021
24022     },
24023     
24024     fireKey: function(e){
24025         if (!this.picker().isVisible()){
24026             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24027                 this.show();
24028             }
24029             return;
24030         }
24031
24032         e.preventDefault();
24033         
24034         switch(e.keyCode){
24035             case 27: // escape
24036                 this.hide();
24037                 break;
24038             case 37: // left
24039             case 39: // right
24040                 this.onTogglePeriod();
24041                 break;
24042             case 38: // up
24043                 this.onIncrementMinutes();
24044                 break;
24045             case 40: // down
24046                 this.onDecrementMinutes();
24047                 break;
24048             case 13: // enter
24049             case 9: // tab
24050                 this.setTime();
24051                 break;
24052         }
24053     },
24054     
24055     onClick: function(e) {
24056         e.stopPropagation();
24057         e.preventDefault();
24058     },
24059     
24060     picker : function()
24061     {
24062         return this.pickerEl;
24063     },
24064     
24065     fillTime: function()
24066     {    
24067         var time = this.pop.select('tbody', true).first();
24068         
24069         time.dom.innerHTML = '';
24070         
24071         time.createChild({
24072             tag: 'tr',
24073             cn: [
24074                 {
24075                     tag: 'td',
24076                     cn: [
24077                         {
24078                             tag: 'a',
24079                             href: '#',
24080                             cls: 'btn',
24081                             cn: [
24082                                 {
24083                                     tag: 'i',
24084                                     cls: 'hours-up fa fas fa-chevron-up'
24085                                 }
24086                             ]
24087                         } 
24088                     ]
24089                 },
24090                 {
24091                     tag: 'td',
24092                     cls: 'separator'
24093                 },
24094                 {
24095                     tag: 'td',
24096                     cn: [
24097                         {
24098                             tag: 'a',
24099                             href: '#',
24100                             cls: 'btn',
24101                             cn: [
24102                                 {
24103                                     tag: 'i',
24104                                     cls: 'minutes-up fa fas fa-chevron-up'
24105                                 }
24106                             ]
24107                         }
24108                     ]
24109                 },
24110                 {
24111                     tag: 'td',
24112                     cls: 'separator'
24113                 }
24114             ]
24115         });
24116         
24117         time.createChild({
24118             tag: 'tr',
24119             cn: [
24120                 {
24121                     tag: 'td',
24122                     cn: [
24123                         {
24124                             tag: 'span',
24125                             cls: 'timepicker-hour',
24126                             html: '00'
24127                         }  
24128                     ]
24129                 },
24130                 {
24131                     tag: 'td',
24132                     cls: 'separator',
24133                     html: ':'
24134                 },
24135                 {
24136                     tag: 'td',
24137                     cn: [
24138                         {
24139                             tag: 'span',
24140                             cls: 'timepicker-minute',
24141                             html: '00'
24142                         }  
24143                     ]
24144                 },
24145                 {
24146                     tag: 'td',
24147                     cls: 'separator'
24148                 },
24149                 {
24150                     tag: 'td',
24151                     cn: [
24152                         {
24153                             tag: 'button',
24154                             type: 'button',
24155                             cls: 'btn btn-primary period',
24156                             html: 'AM'
24157                             
24158                         }
24159                     ]
24160                 }
24161             ]
24162         });
24163         
24164         time.createChild({
24165             tag: 'tr',
24166             cn: [
24167                 {
24168                     tag: 'td',
24169                     cn: [
24170                         {
24171                             tag: 'a',
24172                             href: '#',
24173                             cls: 'btn',
24174                             cn: [
24175                                 {
24176                                     tag: 'span',
24177                                     cls: 'hours-down fa fas fa-chevron-down'
24178                                 }
24179                             ]
24180                         }
24181                     ]
24182                 },
24183                 {
24184                     tag: 'td',
24185                     cls: 'separator'
24186                 },
24187                 {
24188                     tag: 'td',
24189                     cn: [
24190                         {
24191                             tag: 'a',
24192                             href: '#',
24193                             cls: 'btn',
24194                             cn: [
24195                                 {
24196                                     tag: 'span',
24197                                     cls: 'minutes-down fa fas fa-chevron-down'
24198                                 }
24199                             ]
24200                         }
24201                     ]
24202                 },
24203                 {
24204                     tag: 'td',
24205                     cls: 'separator'
24206                 }
24207             ]
24208         });
24209         
24210     },
24211     
24212     update: function()
24213     {
24214         
24215         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24216         
24217         this.fill();
24218     },
24219     
24220     fill: function() 
24221     {
24222         var hours = this.time.getHours();
24223         var minutes = this.time.getMinutes();
24224         var period = 'AM';
24225         
24226         if(hours > 11){
24227             period = 'PM';
24228         }
24229         
24230         if(hours == 0){
24231             hours = 12;
24232         }
24233         
24234         
24235         if(hours > 12){
24236             hours = hours - 12;
24237         }
24238         
24239         if(hours < 10){
24240             hours = '0' + hours;
24241         }
24242         
24243         if(minutes < 10){
24244             minutes = '0' + minutes;
24245         }
24246         
24247         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24248         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24249         this.pop.select('button', true).first().dom.innerHTML = period;
24250         
24251     },
24252     
24253     place: function()
24254     {   
24255         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24256         
24257         var cls = ['bottom'];
24258         
24259         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24260             cls.pop();
24261             cls.push('top');
24262         }
24263         
24264         cls.push('right');
24265         
24266         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24267             cls.pop();
24268             cls.push('left');
24269         }
24270         //this.picker().setXY(20000,20000);
24271         this.picker().addClass(cls.join('-'));
24272         
24273         var _this = this;
24274         
24275         Roo.each(cls, function(c){
24276             if(c == 'bottom'){
24277                 (function() {
24278                  //  
24279                 }).defer(200);
24280                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24281                 //_this.picker().setTop(_this.inputEl().getHeight());
24282                 return;
24283             }
24284             if(c == 'top'){
24285                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24286                 
24287                 //_this.picker().setTop(0 - _this.picker().getHeight());
24288                 return;
24289             }
24290             /*
24291             if(c == 'left'){
24292                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24293                 return;
24294             }
24295             if(c == 'right'){
24296                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24297                 return;
24298             }
24299             */
24300         });
24301         
24302     },
24303   
24304     onFocus : function()
24305     {
24306         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24307         this.show();
24308     },
24309     
24310     onBlur : function()
24311     {
24312         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24313         this.hide();
24314     },
24315     
24316     show : function()
24317     {
24318         this.picker().show();
24319         this.pop.show();
24320         this.update();
24321         this.place();
24322         
24323         this.fireEvent('show', this, this.date);
24324     },
24325     
24326     hide : function()
24327     {
24328         this.picker().hide();
24329         this.pop.hide();
24330         
24331         this.fireEvent('hide', this, this.date);
24332     },
24333     
24334     setTime : function()
24335     {
24336         this.hide();
24337         this.setValue(this.time.format(this.format));
24338         
24339         this.fireEvent('select', this, this.date);
24340         
24341         
24342     },
24343     
24344     onMousedown: function(e){
24345         e.stopPropagation();
24346         e.preventDefault();
24347     },
24348     
24349     onIncrementHours: function()
24350     {
24351         Roo.log('onIncrementHours');
24352         this.time = this.time.add(Date.HOUR, 1);
24353         this.update();
24354         
24355     },
24356     
24357     onDecrementHours: function()
24358     {
24359         Roo.log('onDecrementHours');
24360         this.time = this.time.add(Date.HOUR, -1);
24361         this.update();
24362     },
24363     
24364     onIncrementMinutes: function()
24365     {
24366         Roo.log('onIncrementMinutes');
24367         this.time = this.time.add(Date.MINUTE, 1);
24368         this.update();
24369     },
24370     
24371     onDecrementMinutes: function()
24372     {
24373         Roo.log('onDecrementMinutes');
24374         this.time = this.time.add(Date.MINUTE, -1);
24375         this.update();
24376     },
24377     
24378     onTogglePeriod: function()
24379     {
24380         Roo.log('onTogglePeriod');
24381         this.time = this.time.add(Date.HOUR, 12);
24382         this.update();
24383     }
24384     
24385    
24386 });
24387  
24388
24389 Roo.apply(Roo.bootstrap.form.TimeField,  {
24390   
24391     template : {
24392         tag: 'div',
24393         cls: 'datepicker dropdown-menu',
24394         cn: [
24395             {
24396                 tag: 'div',
24397                 cls: 'datepicker-time',
24398                 cn: [
24399                 {
24400                     tag: 'table',
24401                     cls: 'table-condensed',
24402                     cn:[
24403                         {
24404                             tag: 'tbody',
24405                             cn: [
24406                                 {
24407                                     tag: 'tr',
24408                                     cn: [
24409                                     {
24410                                         tag: 'td',
24411                                         colspan: '7'
24412                                     }
24413                                     ]
24414                                 }
24415                             ]
24416                         },
24417                         {
24418                             tag: 'tfoot',
24419                             cn: [
24420                                 {
24421                                     tag: 'tr',
24422                                     cn: [
24423                                     {
24424                                         tag: 'th',
24425                                         colspan: '7',
24426                                         cls: '',
24427                                         cn: [
24428                                             {
24429                                                 tag: 'button',
24430                                                 cls: 'btn btn-info ok',
24431                                                 html: 'OK'
24432                                             }
24433                                         ]
24434                                     }
24435                     
24436                                     ]
24437                                 }
24438                             ]
24439                         }
24440                     ]
24441                 }
24442                 ]
24443             }
24444         ]
24445     }
24446 });
24447
24448  
24449
24450  /*
24451  * - LGPL
24452  *
24453  * MonthField
24454  * 
24455  */
24456
24457 /**
24458  * @class Roo.bootstrap.form.MonthField
24459  * @extends Roo.bootstrap.form.Input
24460  * Bootstrap MonthField class
24461  * 
24462  * @cfg {String} language default en
24463  * 
24464  * @constructor
24465  * Create a new MonthField
24466  * @param {Object} config The config object
24467  */
24468
24469 Roo.bootstrap.form.MonthField = function(config){
24470     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24471     
24472     this.addEvents({
24473         /**
24474          * @event show
24475          * Fires when this field show.
24476          * @param {Roo.bootstrap.form.MonthField} this
24477          * @param {Mixed} date The date value
24478          */
24479         show : true,
24480         /**
24481          * @event show
24482          * Fires when this field hide.
24483          * @param {Roo.bootstrap.form.MonthField} this
24484          * @param {Mixed} date The date value
24485          */
24486         hide : true,
24487         /**
24488          * @event select
24489          * Fires when select a date.
24490          * @param {Roo.bootstrap.form.MonthField} this
24491          * @param {String} oldvalue The old value
24492          * @param {String} newvalue The new value
24493          */
24494         select : true
24495     });
24496 };
24497
24498 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24499     
24500     onRender: function(ct, position)
24501     {
24502         
24503         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24504         
24505         this.language = this.language || 'en';
24506         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24507         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24508         
24509         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24510         this.isInline = false;
24511         this.isInput = true;
24512         this.component = this.el.select('.add-on', true).first() || false;
24513         this.component = (this.component && this.component.length === 0) ? false : this.component;
24514         this.hasInput = this.component && this.inputEL().length;
24515         
24516         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24517         
24518         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24519         
24520         this.picker().on('mousedown', this.onMousedown, this);
24521         this.picker().on('click', this.onClick, this);
24522         
24523         this.picker().addClass('datepicker-dropdown');
24524         
24525         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24526             v.setStyle('width', '189px');
24527         });
24528         
24529         this.fillMonths();
24530         
24531         this.update();
24532         
24533         if(this.isInline) {
24534             this.show();
24535         }
24536         
24537     },
24538     
24539     setValue: function(v, suppressEvent)
24540     {   
24541         var o = this.getValue();
24542         
24543         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24544         
24545         this.update();
24546
24547         if(suppressEvent !== true){
24548             this.fireEvent('select', this, o, v);
24549         }
24550         
24551     },
24552     
24553     getValue: function()
24554     {
24555         return this.value;
24556     },
24557     
24558     onClick: function(e) 
24559     {
24560         e.stopPropagation();
24561         e.preventDefault();
24562         
24563         var target = e.getTarget();
24564         
24565         if(target.nodeName.toLowerCase() === 'i'){
24566             target = Roo.get(target).dom.parentNode;
24567         }
24568         
24569         var nodeName = target.nodeName;
24570         var className = target.className;
24571         var html = target.innerHTML;
24572         
24573         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24574             return;
24575         }
24576         
24577         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24578         
24579         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24580         
24581         this.hide();
24582                         
24583     },
24584     
24585     picker : function()
24586     {
24587         return this.pickerEl;
24588     },
24589     
24590     fillMonths: function()
24591     {    
24592         var i = 0;
24593         var months = this.picker().select('>.datepicker-months td', true).first();
24594         
24595         months.dom.innerHTML = '';
24596         
24597         while (i < 12) {
24598             var month = {
24599                 tag: 'span',
24600                 cls: 'month',
24601                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24602             };
24603             
24604             months.createChild(month);
24605         }
24606         
24607     },
24608     
24609     update: function()
24610     {
24611         var _this = this;
24612         
24613         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24614             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24615         }
24616         
24617         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24618             e.removeClass('active');
24619             
24620             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24621                 e.addClass('active');
24622             }
24623         })
24624     },
24625     
24626     place: function()
24627     {
24628         if(this.isInline) {
24629             return;
24630         }
24631         
24632         this.picker().removeClass(['bottom', 'top']);
24633         
24634         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24635             /*
24636              * place to the top of element!
24637              *
24638              */
24639             
24640             this.picker().addClass('top');
24641             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24642             
24643             return;
24644         }
24645         
24646         this.picker().addClass('bottom');
24647         
24648         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24649     },
24650     
24651     onFocus : function()
24652     {
24653         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24654         this.show();
24655     },
24656     
24657     onBlur : function()
24658     {
24659         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24660         
24661         var d = this.inputEl().getValue();
24662         
24663         this.setValue(d);
24664                 
24665         this.hide();
24666     },
24667     
24668     show : function()
24669     {
24670         this.picker().show();
24671         this.picker().select('>.datepicker-months', true).first().show();
24672         this.update();
24673         this.place();
24674         
24675         this.fireEvent('show', this, this.date);
24676     },
24677     
24678     hide : function()
24679     {
24680         if(this.isInline) {
24681             return;
24682         }
24683         this.picker().hide();
24684         this.fireEvent('hide', this, this.date);
24685         
24686     },
24687     
24688     onMousedown: function(e)
24689     {
24690         e.stopPropagation();
24691         e.preventDefault();
24692     },
24693     
24694     keyup: function(e)
24695     {
24696         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24697         this.update();
24698     },
24699
24700     fireKey: function(e)
24701     {
24702         if (!this.picker().isVisible()){
24703             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24704                 this.show();
24705             }
24706             return;
24707         }
24708         
24709         var dir;
24710         
24711         switch(e.keyCode){
24712             case 27: // escape
24713                 this.hide();
24714                 e.preventDefault();
24715                 break;
24716             case 37: // left
24717             case 39: // right
24718                 dir = e.keyCode == 37 ? -1 : 1;
24719                 
24720                 this.vIndex = this.vIndex + dir;
24721                 
24722                 if(this.vIndex < 0){
24723                     this.vIndex = 0;
24724                 }
24725                 
24726                 if(this.vIndex > 11){
24727                     this.vIndex = 11;
24728                 }
24729                 
24730                 if(isNaN(this.vIndex)){
24731                     this.vIndex = 0;
24732                 }
24733                 
24734                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24735                 
24736                 break;
24737             case 38: // up
24738             case 40: // down
24739                 
24740                 dir = e.keyCode == 38 ? -1 : 1;
24741                 
24742                 this.vIndex = this.vIndex + dir * 4;
24743                 
24744                 if(this.vIndex < 0){
24745                     this.vIndex = 0;
24746                 }
24747                 
24748                 if(this.vIndex > 11){
24749                     this.vIndex = 11;
24750                 }
24751                 
24752                 if(isNaN(this.vIndex)){
24753                     this.vIndex = 0;
24754                 }
24755                 
24756                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24757                 break;
24758                 
24759             case 13: // enter
24760                 
24761                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24762                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24763                 }
24764                 
24765                 this.hide();
24766                 e.preventDefault();
24767                 break;
24768             case 9: // tab
24769                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24770                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24771                 }
24772                 this.hide();
24773                 break;
24774             case 16: // shift
24775             case 17: // ctrl
24776             case 18: // alt
24777                 break;
24778             default :
24779                 this.hide();
24780                 
24781         }
24782     },
24783     
24784     remove: function() 
24785     {
24786         this.picker().remove();
24787     }
24788    
24789 });
24790
24791 Roo.apply(Roo.bootstrap.form.MonthField,  {
24792     
24793     content : {
24794         tag: 'tbody',
24795         cn: [
24796         {
24797             tag: 'tr',
24798             cn: [
24799             {
24800                 tag: 'td',
24801                 colspan: '7'
24802             }
24803             ]
24804         }
24805         ]
24806     },
24807     
24808     dates:{
24809         en: {
24810             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24811             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24812         }
24813     }
24814 });
24815
24816 Roo.apply(Roo.bootstrap.form.MonthField,  {
24817   
24818     template : {
24819         tag: 'div',
24820         cls: 'datepicker dropdown-menu roo-dynamic',
24821         cn: [
24822             {
24823                 tag: 'div',
24824                 cls: 'datepicker-months',
24825                 cn: [
24826                 {
24827                     tag: 'table',
24828                     cls: 'table-condensed',
24829                     cn:[
24830                         Roo.bootstrap.form.DateField.content
24831                     ]
24832                 }
24833                 ]
24834             }
24835         ]
24836     }
24837 });
24838
24839  
24840
24841  
24842  /*
24843  * - LGPL
24844  *
24845  * CheckBox
24846  * 
24847  */
24848
24849 /**
24850  * @class Roo.bootstrap.form.CheckBox
24851  * @extends Roo.bootstrap.form.Input
24852  * Bootstrap CheckBox class
24853  * 
24854  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24855  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24856  * @cfg {String} boxLabel The text that appears beside the checkbox
24857  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24858  * @cfg {Boolean} checked initnal the element
24859  * @cfg {Boolean} inline inline the element (default false)
24860  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24861  * @cfg {String} tooltip label tooltip
24862  * 
24863  * @constructor
24864  * Create a new CheckBox
24865  * @param {Object} config The config object
24866  */
24867
24868 Roo.bootstrap.form.CheckBox = function(config){
24869     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24870    
24871     this.addEvents({
24872         /**
24873         * @event check
24874         * Fires when the element is checked or unchecked.
24875         * @param {Roo.bootstrap.form.CheckBox} this This input
24876         * @param {Boolean} checked The new checked value
24877         */
24878        check : true,
24879        /**
24880         * @event click
24881         * Fires when the element is click.
24882         * @param {Roo.bootstrap.form.CheckBox} this This input
24883         */
24884        click : true
24885     });
24886     
24887 };
24888
24889 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24890   
24891     inputType: 'checkbox',
24892     inputValue: 1,
24893     valueOff: 0,
24894     boxLabel: false,
24895     checked: false,
24896     weight : false,
24897     inline: false,
24898     tooltip : '',
24899     
24900     // checkbox success does not make any sense really.. 
24901     invalidClass : "",
24902     validClass : "",
24903     
24904     
24905     getAutoCreate : function()
24906     {
24907         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24908         
24909         var id = Roo.id();
24910         
24911         var cfg = {};
24912         
24913         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24914         
24915         if(this.inline){
24916             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24917         }
24918         
24919         var input =  {
24920             tag: 'input',
24921             id : id,
24922             type : this.inputType,
24923             value : this.inputValue,
24924             cls : 'roo-' + this.inputType, //'form-box',
24925             placeholder : this.placeholder || ''
24926             
24927         };
24928         
24929         if(this.inputType != 'radio'){
24930             var hidden =  {
24931                 tag: 'input',
24932                 type : 'hidden',
24933                 cls : 'roo-hidden-value',
24934                 value : this.checked ? this.inputValue : this.valueOff
24935             };
24936         }
24937         
24938             
24939         if (this.weight) { // Validity check?
24940             cfg.cls += " " + this.inputType + "-" + this.weight;
24941         }
24942         
24943         if (this.disabled) {
24944             input.disabled=true;
24945         }
24946         
24947         if(this.checked){
24948             input.checked = this.checked;
24949         }
24950         
24951         if (this.name) {
24952             
24953             input.name = this.name;
24954             
24955             if(this.inputType != 'radio'){
24956                 hidden.name = this.name;
24957                 input.name = '_hidden_' + this.name;
24958             }
24959         }
24960         
24961         if (this.size) {
24962             input.cls += ' input-' + this.size;
24963         }
24964         
24965         var settings=this;
24966         
24967         ['xs','sm','md','lg'].map(function(size){
24968             if (settings[size]) {
24969                 cfg.cls += ' col-' + size + '-' + settings[size];
24970             }
24971         });
24972         
24973         var inputblock = input;
24974          
24975         if (this.before || this.after) {
24976             
24977             inputblock = {
24978                 cls : 'input-group',
24979                 cn :  [] 
24980             };
24981             
24982             if (this.before) {
24983                 inputblock.cn.push({
24984                     tag :'span',
24985                     cls : 'input-group-addon',
24986                     html : this.before
24987                 });
24988             }
24989             
24990             inputblock.cn.push(input);
24991             
24992             if(this.inputType != 'radio'){
24993                 inputblock.cn.push(hidden);
24994             }
24995             
24996             if (this.after) {
24997                 inputblock.cn.push({
24998                     tag :'span',
24999                     cls : 'input-group-addon',
25000                     html : this.after
25001                 });
25002             }
25003             
25004         }
25005         var boxLabelCfg = false;
25006         
25007         if(this.boxLabel){
25008            
25009             boxLabelCfg = {
25010                 tag: 'label',
25011                 //'for': id, // box label is handled by onclick - so no for...
25012                 cls: 'box-label',
25013                 html: this.boxLabel
25014             };
25015             if(this.tooltip){
25016                 boxLabelCfg.tooltip = this.tooltip;
25017             }
25018              
25019         }
25020         
25021         
25022         if (align ==='left' && this.fieldLabel.length) {
25023 //                Roo.log("left and has label");
25024             cfg.cn = [
25025                 {
25026                     tag: 'label',
25027                     'for' :  id,
25028                     cls : 'control-label',
25029                     html : this.fieldLabel
25030                 },
25031                 {
25032                     cls : "", 
25033                     cn: [
25034                         inputblock
25035                     ]
25036                 }
25037             ];
25038             
25039             if (boxLabelCfg) {
25040                 cfg.cn[1].cn.push(boxLabelCfg);
25041             }
25042             
25043             if(this.labelWidth > 12){
25044                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25045             }
25046             
25047             if(this.labelWidth < 13 && this.labelmd == 0){
25048                 this.labelmd = this.labelWidth;
25049             }
25050             
25051             if(this.labellg > 0){
25052                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25053                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25054             }
25055             
25056             if(this.labelmd > 0){
25057                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25058                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25059             }
25060             
25061             if(this.labelsm > 0){
25062                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25063                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25064             }
25065             
25066             if(this.labelxs > 0){
25067                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25068                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25069             }
25070             
25071         } else if ( this.fieldLabel.length) {
25072 //                Roo.log(" label");
25073                 cfg.cn = [
25074                    
25075                     {
25076                         tag: this.boxLabel ? 'span' : 'label',
25077                         'for': id,
25078                         cls: 'control-label box-input-label',
25079                         //cls : 'input-group-addon',
25080                         html : this.fieldLabel
25081                     },
25082                     
25083                     inputblock
25084                     
25085                 ];
25086                 if (boxLabelCfg) {
25087                     cfg.cn.push(boxLabelCfg);
25088                 }
25089
25090         } else {
25091             
25092 //                Roo.log(" no label && no align");
25093                 cfg.cn = [  inputblock ] ;
25094                 if (boxLabelCfg) {
25095                     cfg.cn.push(boxLabelCfg);
25096                 }
25097
25098                 
25099         }
25100         
25101        
25102         
25103         if(this.inputType != 'radio'){
25104             cfg.cn.push(hidden);
25105         }
25106         
25107         return cfg;
25108         
25109     },
25110     
25111     /**
25112      * return the real input element.
25113      */
25114     inputEl: function ()
25115     {
25116         return this.el.select('input.roo-' + this.inputType,true).first();
25117     },
25118     hiddenEl: function ()
25119     {
25120         return this.el.select('input.roo-hidden-value',true).first();
25121     },
25122     
25123     labelEl: function()
25124     {
25125         return this.el.select('label.control-label',true).first();
25126     },
25127     /* depricated... */
25128     
25129     label: function()
25130     {
25131         return this.labelEl();
25132     },
25133     
25134     boxLabelEl: function()
25135     {
25136         return this.el.select('label.box-label',true).first();
25137     },
25138     
25139     initEvents : function()
25140     {
25141 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25142         
25143         this.inputEl().on('click', this.onClick,  this);
25144         
25145         if (this.boxLabel) { 
25146             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25147         }
25148         
25149         this.startValue = this.getValue();
25150         
25151         if(this.groupId){
25152             Roo.bootstrap.form.CheckBox.register(this);
25153         }
25154     },
25155     
25156     onClick : function(e)
25157     {   
25158         if(this.fireEvent('click', this, e) !== false){
25159             this.setChecked(!this.checked);
25160         }
25161         
25162     },
25163     
25164     setChecked : function(state,suppressEvent)
25165     {
25166         this.startValue = this.getValue();
25167
25168         if(this.inputType == 'radio'){
25169             
25170             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25171                 e.dom.checked = false;
25172             });
25173             
25174             this.inputEl().dom.checked = true;
25175             
25176             this.inputEl().dom.value = this.inputValue;
25177             
25178             if(suppressEvent !== true){
25179                 this.fireEvent('check', this, true);
25180             }
25181             
25182             this.validate();
25183             
25184             return;
25185         }
25186         
25187         this.checked = state;
25188         
25189         this.inputEl().dom.checked = state;
25190         
25191         
25192         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25193         
25194         if(suppressEvent !== true){
25195             this.fireEvent('check', this, state);
25196         }
25197         
25198         this.validate();
25199     },
25200     
25201     getValue : function()
25202     {
25203         if(this.inputType == 'radio'){
25204             return this.getGroupValue();
25205         }
25206         
25207         return this.hiddenEl().dom.value;
25208         
25209     },
25210     
25211     getGroupValue : function()
25212     {
25213         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25214             return '';
25215         }
25216         
25217         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25218     },
25219     
25220     setValue : function(v,suppressEvent)
25221     {
25222         if(this.inputType == 'radio'){
25223             this.setGroupValue(v, suppressEvent);
25224             return;
25225         }
25226         
25227         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25228         
25229         this.validate();
25230     },
25231     
25232     setGroupValue : function(v, suppressEvent)
25233     {
25234         this.startValue = this.getValue();
25235         
25236         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25237             e.dom.checked = false;
25238             
25239             if(e.dom.value == v){
25240                 e.dom.checked = true;
25241             }
25242         });
25243         
25244         if(suppressEvent !== true){
25245             this.fireEvent('check', this, true);
25246         }
25247
25248         this.validate();
25249         
25250         return;
25251     },
25252     
25253     validate : function()
25254     {
25255         if(this.getVisibilityEl().hasClass('hidden')){
25256             return true;
25257         }
25258         
25259         if(
25260                 this.disabled || 
25261                 (this.inputType == 'radio' && this.validateRadio()) ||
25262                 (this.inputType == 'checkbox' && this.validateCheckbox())
25263         ){
25264             this.markValid();
25265             return true;
25266         }
25267         
25268         this.markInvalid();
25269         return false;
25270     },
25271     
25272     validateRadio : function()
25273     {
25274         if(this.getVisibilityEl().hasClass('hidden')){
25275             return true;
25276         }
25277         
25278         if(this.allowBlank){
25279             return true;
25280         }
25281         
25282         var valid = false;
25283         
25284         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25285             if(!e.dom.checked){
25286                 return;
25287             }
25288             
25289             valid = true;
25290             
25291             return false;
25292         });
25293         
25294         return valid;
25295     },
25296     
25297     validateCheckbox : function()
25298     {
25299         if(!this.groupId){
25300             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25301             //return (this.getValue() == this.inputValue) ? true : false;
25302         }
25303         
25304         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25305         
25306         if(!group){
25307             return false;
25308         }
25309         
25310         var r = false;
25311         
25312         for(var i in group){
25313             if(group[i].el.isVisible(true)){
25314                 r = false;
25315                 break;
25316             }
25317             
25318             r = true;
25319         }
25320         
25321         for(var i in group){
25322             if(r){
25323                 break;
25324             }
25325             
25326             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25327         }
25328         
25329         return r;
25330     },
25331     
25332     /**
25333      * Mark this field as valid
25334      */
25335     markValid : function()
25336     {
25337         var _this = this;
25338         
25339         this.fireEvent('valid', this);
25340         
25341         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25342         
25343         if(this.groupId){
25344             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25345         }
25346         
25347         if(label){
25348             label.markValid();
25349         }
25350
25351         if(this.inputType == 'radio'){
25352             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25353                 var fg = e.findParent('.form-group', false, true);
25354                 if (Roo.bootstrap.version == 3) {
25355                     fg.removeClass([_this.invalidClass, _this.validClass]);
25356                     fg.addClass(_this.validClass);
25357                 } else {
25358                     fg.removeClass(['is-valid', 'is-invalid']);
25359                     fg.addClass('is-valid');
25360                 }
25361             });
25362             
25363             return;
25364         }
25365
25366         if(!this.groupId){
25367             var fg = this.el.findParent('.form-group', false, true);
25368             if (Roo.bootstrap.version == 3) {
25369                 fg.removeClass([this.invalidClass, this.validClass]);
25370                 fg.addClass(this.validClass);
25371             } else {
25372                 fg.removeClass(['is-valid', 'is-invalid']);
25373                 fg.addClass('is-valid');
25374             }
25375             return;
25376         }
25377         
25378         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25379         
25380         if(!group){
25381             return;
25382         }
25383         
25384         for(var i in group){
25385             var fg = group[i].el.findParent('.form-group', false, true);
25386             if (Roo.bootstrap.version == 3) {
25387                 fg.removeClass([this.invalidClass, this.validClass]);
25388                 fg.addClass(this.validClass);
25389             } else {
25390                 fg.removeClass(['is-valid', 'is-invalid']);
25391                 fg.addClass('is-valid');
25392             }
25393         }
25394     },
25395     
25396      /**
25397      * Mark this field as invalid
25398      * @param {String} msg The validation message
25399      */
25400     markInvalid : function(msg)
25401     {
25402         if(this.allowBlank){
25403             return;
25404         }
25405         
25406         var _this = this;
25407         
25408         this.fireEvent('invalid', this, msg);
25409         
25410         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25411         
25412         if(this.groupId){
25413             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25414         }
25415         
25416         if(label){
25417             label.markInvalid();
25418         }
25419             
25420         if(this.inputType == 'radio'){
25421             
25422             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25423                 var fg = e.findParent('.form-group', false, true);
25424                 if (Roo.bootstrap.version == 3) {
25425                     fg.removeClass([_this.invalidClass, _this.validClass]);
25426                     fg.addClass(_this.invalidClass);
25427                 } else {
25428                     fg.removeClass(['is-invalid', 'is-valid']);
25429                     fg.addClass('is-invalid');
25430                 }
25431             });
25432             
25433             return;
25434         }
25435         
25436         if(!this.groupId){
25437             var fg = this.el.findParent('.form-group', false, true);
25438             if (Roo.bootstrap.version == 3) {
25439                 fg.removeClass([_this.invalidClass, _this.validClass]);
25440                 fg.addClass(_this.invalidClass);
25441             } else {
25442                 fg.removeClass(['is-invalid', 'is-valid']);
25443                 fg.addClass('is-invalid');
25444             }
25445             return;
25446         }
25447         
25448         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25449         
25450         if(!group){
25451             return;
25452         }
25453         
25454         for(var i in group){
25455             var fg = group[i].el.findParent('.form-group', false, true);
25456             if (Roo.bootstrap.version == 3) {
25457                 fg.removeClass([_this.invalidClass, _this.validClass]);
25458                 fg.addClass(_this.invalidClass);
25459             } else {
25460                 fg.removeClass(['is-invalid', 'is-valid']);
25461                 fg.addClass('is-invalid');
25462             }
25463         }
25464         
25465     },
25466     
25467     clearInvalid : function()
25468     {
25469         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25470         
25471         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25472         
25473         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25474         
25475         if (label && label.iconEl) {
25476             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25477             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25478         }
25479     },
25480     
25481     disable : function()
25482     {
25483         if(this.inputType != 'radio'){
25484             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25485             return;
25486         }
25487         
25488         var _this = this;
25489         
25490         if(this.rendered){
25491             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25492                 _this.getActionEl().addClass(this.disabledClass);
25493                 e.dom.disabled = true;
25494             });
25495         }
25496         
25497         this.disabled = true;
25498         this.fireEvent("disable", this);
25499         return this;
25500     },
25501
25502     enable : function()
25503     {
25504         if(this.inputType != 'radio'){
25505             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25506             return;
25507         }
25508         
25509         var _this = this;
25510         
25511         if(this.rendered){
25512             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25513                 _this.getActionEl().removeClass(this.disabledClass);
25514                 e.dom.disabled = false;
25515             });
25516         }
25517         
25518         this.disabled = false;
25519         this.fireEvent("enable", this);
25520         return this;
25521     },
25522     
25523     setBoxLabel : function(v)
25524     {
25525         this.boxLabel = v;
25526         
25527         if(this.rendered){
25528             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25529         }
25530     }
25531
25532 });
25533
25534 Roo.apply(Roo.bootstrap.form.CheckBox, {
25535     
25536     groups: {},
25537     
25538      /**
25539     * register a CheckBox Group
25540     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25541     */
25542     register : function(checkbox)
25543     {
25544         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25545             this.groups[checkbox.groupId] = {};
25546         }
25547         
25548         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25549             return;
25550         }
25551         
25552         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25553         
25554     },
25555     /**
25556     * fetch a CheckBox Group based on the group ID
25557     * @param {string} the group ID
25558     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25559     */
25560     get: function(groupId) {
25561         if (typeof(this.groups[groupId]) == 'undefined') {
25562             return false;
25563         }
25564         
25565         return this.groups[groupId] ;
25566     }
25567     
25568     
25569 });
25570 /*
25571  * - LGPL
25572  *
25573  * RadioItem
25574  * 
25575  */
25576
25577 /**
25578  * @class Roo.bootstrap.form.Radio
25579  * @extends Roo.bootstrap.Component
25580  * Bootstrap Radio class
25581  * @cfg {String} boxLabel - the label associated
25582  * @cfg {String} value - the value of radio
25583  * 
25584  * @constructor
25585  * Create a new Radio
25586  * @param {Object} config The config object
25587  */
25588 Roo.bootstrap.form.Radio = function(config){
25589     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25590     
25591 };
25592
25593 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25594     
25595     boxLabel : '',
25596     
25597     value : '',
25598     
25599     getAutoCreate : function()
25600     {
25601         var cfg = {
25602             tag : 'div',
25603             cls : 'form-group radio',
25604             cn : [
25605                 {
25606                     tag : 'label',
25607                     cls : 'box-label',
25608                     html : this.boxLabel
25609                 }
25610             ]
25611         };
25612         
25613         return cfg;
25614     },
25615     
25616     initEvents : function() 
25617     {
25618         this.parent().register(this);
25619         
25620         this.el.on('click', this.onClick, this);
25621         
25622     },
25623     
25624     onClick : function(e)
25625     {
25626         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25627             this.setChecked(true);
25628         }
25629     },
25630     
25631     setChecked : function(state, suppressEvent)
25632     {
25633         this.parent().setValue(this.value, suppressEvent);
25634         
25635     },
25636     
25637     setBoxLabel : function(v)
25638     {
25639         this.boxLabel = v;
25640         
25641         if(this.rendered){
25642             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25643         }
25644     }
25645     
25646 });
25647  
25648
25649  /*
25650  * - LGPL
25651  *
25652  * Input
25653  * 
25654  */
25655
25656 /**
25657  * @class Roo.bootstrap.form.SecurePass
25658  * @extends Roo.bootstrap.form.Input
25659  * Bootstrap SecurePass class
25660  *
25661  * 
25662  * @constructor
25663  * Create a new SecurePass
25664  * @param {Object} config The config object
25665  */
25666  
25667 Roo.bootstrap.form.SecurePass = function (config) {
25668     // these go here, so the translation tool can replace them..
25669     this.errors = {
25670         PwdEmpty: "Please type a password, and then retype it to confirm.",
25671         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25672         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25673         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25674         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25675         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25676         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25677         TooWeak: "Your password is Too Weak."
25678     },
25679     this.meterLabel = "Password strength:";
25680     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25681     this.meterClass = [
25682         "roo-password-meter-tooweak", 
25683         "roo-password-meter-weak", 
25684         "roo-password-meter-medium", 
25685         "roo-password-meter-strong", 
25686         "roo-password-meter-grey"
25687     ];
25688     
25689     this.errors = {};
25690     
25691     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25692 }
25693
25694 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25695     /**
25696      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25697      * {
25698      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25699      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25700      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25701      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25702      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25703      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25704      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25705      * })
25706      */
25707     // private
25708     
25709     meterWidth: 300,
25710     errorMsg :'',    
25711     errors: false,
25712     imageRoot: '/',
25713     /**
25714      * @cfg {String/Object} Label for the strength meter (defaults to
25715      * 'Password strength:')
25716      */
25717     // private
25718     meterLabel: '',
25719     /**
25720      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25721      * ['Weak', 'Medium', 'Strong'])
25722      */
25723     // private    
25724     pwdStrengths: false,    
25725     // private
25726     strength: 0,
25727     // private
25728     _lastPwd: null,
25729     // private
25730     kCapitalLetter: 0,
25731     kSmallLetter: 1,
25732     kDigit: 2,
25733     kPunctuation: 3,
25734     
25735     insecure: false,
25736     // private
25737     initEvents: function ()
25738     {
25739         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25740
25741         if (this.el.is('input[type=password]') && Roo.isSafari) {
25742             this.el.on('keydown', this.SafariOnKeyDown, this);
25743         }
25744
25745         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25746     },
25747     // private
25748     onRender: function (ct, position)
25749     {
25750         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25751         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25752         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25753
25754         this.trigger.createChild({
25755                    cn: [
25756                     {
25757                     //id: 'PwdMeter',
25758                     tag: 'div',
25759                     cls: 'roo-password-meter-grey col-xs-12',
25760                     style: {
25761                         //width: 0,
25762                         //width: this.meterWidth + 'px'                                                
25763                         }
25764                     },
25765                     {                            
25766                          cls: 'roo-password-meter-text'                          
25767                     }
25768                 ]            
25769         });
25770
25771          
25772         if (this.hideTrigger) {
25773             this.trigger.setDisplayed(false);
25774         }
25775         this.setSize(this.width || '', this.height || '');
25776     },
25777     // private
25778     onDestroy: function ()
25779     {
25780         if (this.trigger) {
25781             this.trigger.removeAllListeners();
25782             this.trigger.remove();
25783         }
25784         if (this.wrap) {
25785             this.wrap.remove();
25786         }
25787         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25788     },
25789     // private
25790     checkStrength: function ()
25791     {
25792         var pwd = this.inputEl().getValue();
25793         if (pwd == this._lastPwd) {
25794             return;
25795         }
25796
25797         var strength;
25798         if (this.ClientSideStrongPassword(pwd)) {
25799             strength = 3;
25800         } else if (this.ClientSideMediumPassword(pwd)) {
25801             strength = 2;
25802         } else if (this.ClientSideWeakPassword(pwd)) {
25803             strength = 1;
25804         } else {
25805             strength = 0;
25806         }
25807         
25808         Roo.log('strength1: ' + strength);
25809         
25810         //var pm = this.trigger.child('div/div/div').dom;
25811         var pm = this.trigger.child('div/div');
25812         pm.removeClass(this.meterClass);
25813         pm.addClass(this.meterClass[strength]);
25814                 
25815         
25816         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25817                 
25818         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25819         
25820         this._lastPwd = pwd;
25821     },
25822     reset: function ()
25823     {
25824         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25825         
25826         this._lastPwd = '';
25827         
25828         var pm = this.trigger.child('div/div');
25829         pm.removeClass(this.meterClass);
25830         pm.addClass('roo-password-meter-grey');        
25831         
25832         
25833         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25834         
25835         pt.innerHTML = '';
25836         this.inputEl().dom.type='password';
25837     },
25838     // private
25839     validateValue: function (value)
25840     {
25841         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25842             return false;
25843         }
25844         if (value.length == 0) {
25845             if (this.allowBlank) {
25846                 this.clearInvalid();
25847                 return true;
25848             }
25849
25850             this.markInvalid(this.errors.PwdEmpty);
25851             this.errorMsg = this.errors.PwdEmpty;
25852             return false;
25853         }
25854         
25855         if(this.insecure){
25856             return true;
25857         }
25858         
25859         if (!value.match(/[\x21-\x7e]+/)) {
25860             this.markInvalid(this.errors.PwdBadChar);
25861             this.errorMsg = this.errors.PwdBadChar;
25862             return false;
25863         }
25864         if (value.length < 6) {
25865             this.markInvalid(this.errors.PwdShort);
25866             this.errorMsg = this.errors.PwdShort;
25867             return false;
25868         }
25869         if (value.length > 16) {
25870             this.markInvalid(this.errors.PwdLong);
25871             this.errorMsg = this.errors.PwdLong;
25872             return false;
25873         }
25874         var strength;
25875         if (this.ClientSideStrongPassword(value)) {
25876             strength = 3;
25877         } else if (this.ClientSideMediumPassword(value)) {
25878             strength = 2;
25879         } else if (this.ClientSideWeakPassword(value)) {
25880             strength = 1;
25881         } else {
25882             strength = 0;
25883         }
25884
25885         
25886         if (strength < 2) {
25887             //this.markInvalid(this.errors.TooWeak);
25888             this.errorMsg = this.errors.TooWeak;
25889             //return false;
25890         }
25891         
25892         
25893         console.log('strength2: ' + strength);
25894         
25895         //var pm = this.trigger.child('div/div/div').dom;
25896         
25897         var pm = this.trigger.child('div/div');
25898         pm.removeClass(this.meterClass);
25899         pm.addClass(this.meterClass[strength]);
25900                 
25901         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25902                 
25903         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25904         
25905         this.errorMsg = ''; 
25906         return true;
25907     },
25908     // private
25909     CharacterSetChecks: function (type)
25910     {
25911         this.type = type;
25912         this.fResult = false;
25913     },
25914     // private
25915     isctype: function (character, type)
25916     {
25917         switch (type) {  
25918             case this.kCapitalLetter:
25919                 if (character >= 'A' && character <= 'Z') {
25920                     return true;
25921                 }
25922                 break;
25923             
25924             case this.kSmallLetter:
25925                 if (character >= 'a' && character <= 'z') {
25926                     return true;
25927                 }
25928                 break;
25929             
25930             case this.kDigit:
25931                 if (character >= '0' && character <= '9') {
25932                     return true;
25933                 }
25934                 break;
25935             
25936             case this.kPunctuation:
25937                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25938                     return true;
25939                 }
25940                 break;
25941             
25942             default:
25943                 return false;
25944         }
25945
25946     },
25947     // private
25948     IsLongEnough: function (pwd, size)
25949     {
25950         return !(pwd == null || isNaN(size) || pwd.length < size);
25951     },
25952     // private
25953     SpansEnoughCharacterSets: function (word, nb)
25954     {
25955         if (!this.IsLongEnough(word, nb))
25956         {
25957             return false;
25958         }
25959
25960         var characterSetChecks = new Array(
25961             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25962             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25963         );
25964         
25965         for (var index = 0; index < word.length; ++index) {
25966             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25967                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25968                     characterSetChecks[nCharSet].fResult = true;
25969                     break;
25970                 }
25971             }
25972         }
25973
25974         var nCharSets = 0;
25975         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25976             if (characterSetChecks[nCharSet].fResult) {
25977                 ++nCharSets;
25978             }
25979         }
25980
25981         if (nCharSets < nb) {
25982             return false;
25983         }
25984         return true;
25985     },
25986     // private
25987     ClientSideStrongPassword: function (pwd)
25988     {
25989         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25990     },
25991     // private
25992     ClientSideMediumPassword: function (pwd)
25993     {
25994         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25995     },
25996     // private
25997     ClientSideWeakPassword: function (pwd)
25998     {
25999         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26000     }
26001           
26002 });
26003 Roo.htmleditor = {};
26004  
26005 /**
26006  * @class Roo.htmleditor.Filter
26007  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26008  * @cfg {DomElement} node The node to iterate and filter
26009  * @cfg {boolean|String|Array} tag Tags to replace 
26010  * @constructor
26011  * Create a new Filter.
26012  * @param {Object} config Configuration options
26013  */
26014
26015
26016
26017 Roo.htmleditor.Filter = function(cfg) {
26018     Roo.apply(this.cfg);
26019     // this does not actually call walk as it's really just a abstract class
26020 }
26021
26022
26023 Roo.htmleditor.Filter.prototype = {
26024     
26025     node: false,
26026     
26027     tag: false,
26028
26029     // overrride to do replace comments.
26030     replaceComment : false,
26031     
26032     // overrride to do replace or do stuff with tags..
26033     replaceTag : false,
26034     
26035     walk : function(dom)
26036     {
26037         Roo.each( Array.from(dom.childNodes), function( e ) {
26038             switch(true) {
26039                 
26040                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26041                     this.replaceComment(e);
26042                     return;
26043                 
26044                 case e.nodeType != 1: //not a node.
26045                     return;
26046                 
26047                 case this.tag === true: // everything
26048                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26049                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26050                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26051                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26052                     if (this.replaceTag && false === this.replaceTag(e)) {
26053                         return;
26054                     }
26055                     if (e.hasChildNodes()) {
26056                         this.walk(e);
26057                     }
26058                     return;
26059                 
26060                 default:    // tags .. that do not match.
26061                     if (e.hasChildNodes()) {
26062                         this.walk(e);
26063                     }
26064             }
26065             
26066         }, this);
26067         
26068     },
26069     
26070     
26071     removeNodeKeepChildren : function( node)
26072     {
26073     
26074         ar = Array.from(node.childNodes);
26075         for (var i = 0; i < ar.length; i++) {
26076          
26077             node.removeChild(ar[i]);
26078             // what if we need to walk these???
26079             node.parentNode.insertBefore(ar[i], node);
26080            
26081         }
26082         node.parentNode.removeChild(node);
26083     }
26084 }; 
26085
26086 /**
26087  * @class Roo.htmleditor.FilterAttributes
26088  * clean attributes and  styles including http:// etc.. in attribute
26089  * @constructor
26090 * Run a new Attribute Filter
26091 * @param {Object} config Configuration options
26092  */
26093 Roo.htmleditor.FilterAttributes = function(cfg)
26094 {
26095     Roo.apply(this, cfg);
26096     this.attrib_black = this.attrib_black || [];
26097     this.attrib_white = this.attrib_white || [];
26098
26099     this.attrib_clean = this.attrib_clean || [];
26100     this.style_white = this.style_white || [];
26101     this.style_black = this.style_black || [];
26102     this.walk(cfg.node);
26103 }
26104
26105 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26106 {
26107     tag: true, // all tags
26108     
26109     attrib_black : false, // array
26110     attrib_clean : false,
26111     attrib_white : false,
26112
26113     style_white : false,
26114     style_black : false,
26115      
26116      
26117     replaceTag : function(node)
26118     {
26119         if (!node.attributes || !node.attributes.length) {
26120             return true;
26121         }
26122         
26123         for (var i = node.attributes.length-1; i > -1 ; i--) {
26124             var a = node.attributes[i];
26125             //console.log(a);
26126             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26127                 node.removeAttribute(a.name);
26128                 continue;
26129             }
26130             
26131             
26132             
26133             if (a.name.toLowerCase().substr(0,2)=='on')  {
26134                 node.removeAttribute(a.name);
26135                 continue;
26136             }
26137             
26138             
26139             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26140                 node.removeAttribute(a.name);
26141                 continue;
26142             }
26143             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26144                 this.cleanAttr(node,a.name,a.value); // fixme..
26145                 continue;
26146             }
26147             if (a.name == 'style') {
26148                 this.cleanStyle(node,a.name,a.value);
26149                 continue;
26150             }
26151             /// clean up MS crap..
26152             // tecnically this should be a list of valid class'es..
26153             
26154             
26155             if (a.name == 'class') {
26156                 if (a.value.match(/^Mso/)) {
26157                     node.removeAttribute('class');
26158                 }
26159                 
26160                 if (a.value.match(/^body$/)) {
26161                     node.removeAttribute('class');
26162                 }
26163                 continue;
26164             }
26165             
26166             
26167             // style cleanup!?
26168             // class cleanup?
26169             
26170         }
26171         return true; // clean children
26172     },
26173         
26174     cleanAttr: function(node, n,v)
26175     {
26176         
26177         if (v.match(/^\./) || v.match(/^\//)) {
26178             return;
26179         }
26180         if (v.match(/^(http|https):\/\//)
26181             || v.match(/^mailto:/) 
26182             || v.match(/^ftp:/)
26183             || v.match(/^data:/)
26184             ) {
26185             return;
26186         }
26187         if (v.match(/^#/)) {
26188             return;
26189         }
26190         if (v.match(/^\{/)) { // allow template editing.
26191             return;
26192         }
26193 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26194         node.removeAttribute(n);
26195         
26196     },
26197     cleanStyle : function(node,  n,v)
26198     {
26199         if (v.match(/expression/)) { //XSS?? should we even bother..
26200             node.removeAttribute(n);
26201             return;
26202         }
26203         
26204         var parts = v.split(/;/);
26205         var clean = [];
26206         
26207         Roo.each(parts, function(p) {
26208             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26209             if (!p.length) {
26210                 return true;
26211             }
26212             var l = p.split(':').shift().replace(/\s+/g,'');
26213             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26214             
26215             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26216                 return true;
26217             }
26218             //Roo.log()
26219             // only allow 'c whitelisted system attributes'
26220             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26221                 return true;
26222             }
26223             
26224             
26225             clean.push(p);
26226             return true;
26227         },this);
26228         if (clean.length) { 
26229             node.setAttribute(n, clean.join(';'));
26230         } else {
26231             node.removeAttribute(n);
26232         }
26233         
26234     }
26235         
26236         
26237         
26238     
26239 });/**
26240  * @class Roo.htmleditor.FilterBlack
26241  * remove blacklisted elements.
26242  * @constructor
26243  * Run a new Blacklisted Filter
26244  * @param {Object} config Configuration options
26245  */
26246
26247 Roo.htmleditor.FilterBlack = function(cfg)
26248 {
26249     Roo.apply(this, cfg);
26250     this.walk(cfg.node);
26251 }
26252
26253 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26254 {
26255     tag : true, // all elements.
26256    
26257     replaceTag : function(n)
26258     {
26259         n.parentNode.removeChild(n);
26260     }
26261 });
26262 /**
26263  * @class Roo.htmleditor.FilterComment
26264  * remove comments.
26265  * @constructor
26266 * Run a new Comments Filter
26267 * @param {Object} config Configuration options
26268  */
26269 Roo.htmleditor.FilterComment = function(cfg)
26270 {
26271     this.walk(cfg.node);
26272 }
26273
26274 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26275 {
26276   
26277     replaceComment : function(n)
26278     {
26279         n.parentNode.removeChild(n);
26280     }
26281 });/**
26282  * @class Roo.htmleditor.FilterKeepChildren
26283  * remove tags but keep children
26284  * @constructor
26285  * Run a new Keep Children Filter
26286  * @param {Object} config Configuration options
26287  */
26288
26289 Roo.htmleditor.FilterKeepChildren = function(cfg)
26290 {
26291     Roo.apply(this, cfg);
26292     if (this.tag === false) {
26293         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26294     }
26295     // hacky?
26296     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26297         this.cleanNamespace = true;
26298     }
26299         
26300     this.walk(cfg.node);
26301 }
26302
26303 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26304 {
26305     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26306   
26307     replaceTag : function(node)
26308     {
26309         // walk children...
26310         //Roo.log(node.tagName);
26311         var ar = Array.from(node.childNodes);
26312         //remove first..
26313         
26314         for (var i = 0; i < ar.length; i++) {
26315             var e = ar[i];
26316             if (e.nodeType == 1) {
26317                 if (
26318                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26319                     || // array and it matches
26320                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
26321                     ||
26322                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26323                     ||
26324                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26325                 ) {
26326                     this.replaceTag(ar[i]); // child is blacklisted as well...
26327                     continue;
26328                 }
26329             }
26330         }  
26331         ar = Array.from(node.childNodes);
26332         for (var i = 0; i < ar.length; i++) {
26333          
26334             node.removeChild(ar[i]);
26335             // what if we need to walk these???
26336             node.parentNode.insertBefore(ar[i], node);
26337             if (this.tag !== false) {
26338                 this.walk(ar[i]);
26339                 
26340             }
26341         }
26342         //Roo.log("REMOVE:" + node.tagName);
26343         node.parentNode.removeChild(node);
26344         return false; // don't walk children
26345         
26346         
26347     }
26348 });/**
26349  * @class Roo.htmleditor.FilterParagraph
26350  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26351  * like on 'push' to remove the <p> tags and replace them with line breaks.
26352  * @constructor
26353  * Run a new Paragraph Filter
26354  * @param {Object} config Configuration options
26355  */
26356
26357 Roo.htmleditor.FilterParagraph = function(cfg)
26358 {
26359     // no need to apply config.
26360     this.walk(cfg.node);
26361 }
26362
26363 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26364 {
26365     
26366      
26367     tag : 'P',
26368     
26369      
26370     replaceTag : function(node)
26371     {
26372         
26373         if (node.childNodes.length == 1 &&
26374             node.childNodes[0].nodeType == 3 &&
26375             node.childNodes[0].textContent.trim().length < 1
26376             ) {
26377             // remove and replace with '<BR>';
26378             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26379             return false; // no need to walk..
26380         }
26381         var ar = Array.from(node.childNodes);
26382         for (var i = 0; i < ar.length; i++) {
26383             node.removeChild(ar[i]);
26384             // what if we need to walk these???
26385             node.parentNode.insertBefore(ar[i], node);
26386         }
26387         // now what about this?
26388         // <p> &nbsp; </p>
26389         
26390         // double BR.
26391         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26392         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26393         node.parentNode.removeChild(node);
26394         
26395         return false;
26396
26397     }
26398     
26399 });/**
26400  * @class Roo.htmleditor.FilterSpan
26401  * filter span's with no attributes out..
26402  * @constructor
26403  * Run a new Span Filter
26404  * @param {Object} config Configuration options
26405  */
26406
26407 Roo.htmleditor.FilterSpan = function(cfg)
26408 {
26409     // no need to apply config.
26410     this.walk(cfg.node);
26411 }
26412
26413 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26414 {
26415      
26416     tag : 'SPAN',
26417      
26418  
26419     replaceTag : function(node)
26420     {
26421         if (node.attributes && node.attributes.length > 0) {
26422             return true; // walk if there are any.
26423         }
26424         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26425         return false;
26426      
26427     }
26428     
26429 });/**
26430  * @class Roo.htmleditor.FilterTableWidth
26431   try and remove table width data - as that frequently messes up other stuff.
26432  * 
26433  *      was cleanTableWidths.
26434  *
26435  * Quite often pasting from word etc.. results in tables with column and widths.
26436  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26437  *
26438  * @constructor
26439  * Run a new Table Filter
26440  * @param {Object} config Configuration options
26441  */
26442
26443 Roo.htmleditor.FilterTableWidth = function(cfg)
26444 {
26445     // no need to apply config.
26446     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26447     this.walk(cfg.node);
26448 }
26449
26450 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26451 {
26452      
26453      
26454     
26455     replaceTag: function(node) {
26456         
26457         
26458       
26459         if (node.hasAttribute('width')) {
26460             node.removeAttribute('width');
26461         }
26462         
26463          
26464         if (node.hasAttribute("style")) {
26465             // pretty basic...
26466             
26467             var styles = node.getAttribute("style").split(";");
26468             var nstyle = [];
26469             Roo.each(styles, function(s) {
26470                 if (!s.match(/:/)) {
26471                     return;
26472                 }
26473                 var kv = s.split(":");
26474                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26475                     return;
26476                 }
26477                 // what ever is left... we allow.
26478                 nstyle.push(s);
26479             });
26480             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26481             if (!nstyle.length) {
26482                 node.removeAttribute('style');
26483             }
26484         }
26485         
26486         return true; // continue doing children..
26487     }
26488 });/**
26489  * @class Roo.htmleditor.FilterWord
26490  * try and clean up all the mess that Word generates.
26491  * 
26492  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
26493  
26494  * @constructor
26495  * Run a new Span Filter
26496  * @param {Object} config Configuration options
26497  */
26498
26499 Roo.htmleditor.FilterWord = function(cfg)
26500 {
26501     // no need to apply config.
26502     this.replaceDocBullets(cfg.node);
26503     
26504     this.replaceAname(cfg.node);
26505     // this is disabled as the removal is done by other filters;
26506    // this.walk(cfg.node);
26507     
26508     
26509 }
26510
26511 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
26512 {
26513     tag: true,
26514      
26515     
26516     /**
26517      * Clean up MS wordisms...
26518      */
26519     replaceTag : function(node)
26520     {
26521          
26522         // no idea what this does - span with text, replaceds with just text.
26523         if(
26524                 node.nodeName == 'SPAN' &&
26525                 !node.hasAttributes() &&
26526                 node.childNodes.length == 1 &&
26527                 node.firstChild.nodeName == "#text"  
26528         ) {
26529             var textNode = node.firstChild;
26530             node.removeChild(textNode);
26531             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26532                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26533             }
26534             node.parentNode.insertBefore(textNode, node);
26535             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26536                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26537             }
26538             
26539             node.parentNode.removeChild(node);
26540             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
26541         }
26542         
26543    
26544         
26545         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26546             node.parentNode.removeChild(node);
26547             return false; // dont do chidlren
26548         }
26549         //Roo.log(node.tagName);
26550         // remove - but keep children..
26551         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26552             //Roo.log('-- removed');
26553             while (node.childNodes.length) {
26554                 var cn = node.childNodes[0];
26555                 node.removeChild(cn);
26556                 node.parentNode.insertBefore(cn, node);
26557                 // move node to parent - and clean it..
26558                 if (cn.nodeType == 1) {
26559                     this.replaceTag(cn);
26560                 }
26561                 
26562             }
26563             node.parentNode.removeChild(node);
26564             /// no need to iterate chidlren = it's got none..
26565             //this.iterateChildren(node, this.cleanWord);
26566             return false; // no need to iterate children.
26567         }
26568         // clean styles
26569         if (node.className.length) {
26570             
26571             var cn = node.className.split(/\W+/);
26572             var cna = [];
26573             Roo.each(cn, function(cls) {
26574                 if (cls.match(/Mso[a-zA-Z]+/)) {
26575                     return;
26576                 }
26577                 cna.push(cls);
26578             });
26579             node.className = cna.length ? cna.join(' ') : '';
26580             if (!cna.length) {
26581                 node.removeAttribute("class");
26582             }
26583         }
26584         
26585         if (node.hasAttribute("lang")) {
26586             node.removeAttribute("lang");
26587         }
26588         
26589         if (node.hasAttribute("style")) {
26590             
26591             var styles = node.getAttribute("style").split(";");
26592             var nstyle = [];
26593             Roo.each(styles, function(s) {
26594                 if (!s.match(/:/)) {
26595                     return;
26596                 }
26597                 var kv = s.split(":");
26598                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26599                     return;
26600                 }
26601                 // what ever is left... we allow.
26602                 nstyle.push(s);
26603             });
26604             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26605             if (!nstyle.length) {
26606                 node.removeAttribute('style');
26607             }
26608         }
26609         return true; // do children
26610         
26611         
26612         
26613     },
26614     
26615     styleToObject: function(node)
26616     {
26617         var styles = (node.getAttribute("style") || '').split(";");
26618         var ret = {};
26619         Roo.each(styles, function(s) {
26620             if (!s.match(/:/)) {
26621                 return;
26622             }
26623             var kv = s.split(":");
26624              
26625             // what ever is left... we allow.
26626             ret[kv[0].trim()] = kv[1];
26627         });
26628         return ret;
26629     },
26630     
26631     
26632     replaceAname : function (doc)
26633     {
26634         // replace all the a/name without..
26635         var aa = Array.from(doc.getElementsByTagName('a'));
26636         for (var i = 0; i  < aa.length; i++) {
26637             var a = aa[i];
26638             if (a.hasAttribute("name")) {
26639                 a.removeAttribute("name");
26640             }
26641             if (a.hasAttribute("href")) {
26642                 continue;
26643             }
26644             // reparent children.
26645             this.removeNodeKeepChildren(a);
26646             
26647         }
26648         
26649         
26650         
26651     },
26652
26653     
26654     
26655     replaceDocBullets : function(doc)
26656     {
26657         // this is a bit odd - but it appears some indents use ql-indent-1
26658          //Roo.log(doc.innerHTML);
26659         
26660         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
26661         for( var i = 0; i < listpara.length; i ++) {
26662             listpara[i].className = "MsoListParagraph";
26663         }
26664         
26665         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
26666         for( var i = 0; i < listpara.length; i ++) {
26667             listpara[i].className = "MsoListParagraph";
26668         }
26669         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
26670         for( var i = 0; i < listpara.length; i ++) {
26671             listpara[i].className = "MsoListParagraph";
26672         }
26673         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
26674         for( var i = 0; i < listpara.length; i ++) {
26675             listpara[i].className = "MsoListParagraph";
26676         }
26677         
26678         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
26679         var htwo =  Array.from(doc.getElementsByTagName('h2'));
26680         for( var i = 0; i < htwo.length; i ++) {
26681             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
26682                 htwo[i].className = "MsoListParagraph";
26683             }
26684         }
26685         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
26686         for( var i = 0; i < listpara.length; i ++) {
26687             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
26688                 listpara[i].className = "MsoListParagraph";
26689             } else {
26690                 listpara[i].className = "MsoNormalx";
26691             }
26692         }
26693        
26694         listpara = doc.getElementsByClassName('MsoListParagraph');
26695         // Roo.log(doc.innerHTML);
26696         
26697         
26698         
26699         while(listpara.length) {
26700             
26701             this.replaceDocBullet(listpara.item(0));
26702         }
26703       
26704     },
26705     
26706      
26707     
26708     replaceDocBullet : function(p)
26709     {
26710         // gather all the siblings.
26711         var ns = p,
26712             parent = p.parentNode,
26713             doc = parent.ownerDocument,
26714             items = [];
26715             
26716         var listtype = 'ul';   
26717         while (ns) {
26718             if (ns.nodeType != 1) {
26719                 ns = ns.nextSibling;
26720                 continue;
26721             }
26722             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
26723                 break;
26724             }
26725             var spans = ns.getElementsByTagName('span');
26726             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
26727                 items.push(ns);
26728                 ns = ns.nextSibling;
26729                 has_list = true;
26730                 if (spans.length && spans[0].hasAttribute('style')) {
26731                     var  style = this.styleToObject(spans[0]);
26732                     if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
26733                         listtype = 'ol';
26734                     }
26735                 }
26736                 
26737                 continue;
26738             }
26739             var spans = ns.getElementsByTagName('span');
26740             if (!spans.length) {
26741                 break;
26742             }
26743             var has_list  = false;
26744             for(var i = 0; i < spans.length; i++) {
26745                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
26746                     has_list = true;
26747                     break;
26748                 }
26749             }
26750             if (!has_list) {
26751                 break;
26752             }
26753             items.push(ns);
26754             ns = ns.nextSibling;
26755             
26756             
26757         }
26758         if (!items.length) {
26759             ns.className = "";
26760             return;
26761         }
26762         
26763         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
26764         parent.insertBefore(ul, p);
26765         var lvl = 0;
26766         var stack = [ ul ];
26767         var last_li = false;
26768         
26769         var margin_to_depth = {};
26770         max_margins = -1;
26771         
26772         items.forEach(function(n, ipos) {
26773             //Roo.log("got innertHMLT=" + n.innerHTML);
26774             
26775             var spans = n.getElementsByTagName('span');
26776             if (!spans.length) {
26777                 //Roo.log("No spans found");
26778                  
26779                 parent.removeChild(n);
26780                 
26781                 
26782                 return; // skip it...
26783             }
26784            
26785                 
26786             var num = 1;
26787             var style = {};
26788             for(var i = 0; i < spans.length; i++) {
26789             
26790                 style = this.styleToObject(spans[i]);
26791                 if (typeof(style['mso-list']) == 'undefined') {
26792                     continue;
26793                 }
26794                 if (listtype == 'ol') {
26795                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
26796                 }
26797                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
26798                 break;
26799             }
26800             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
26801             style = this.styleToObject(n); // mo-list is from the parent node.
26802             if (typeof(style['mso-list']) == 'undefined') {
26803                 //Roo.log("parent is missing level");
26804                   
26805                 parent.removeChild(n);
26806                  
26807                 return;
26808             }
26809             
26810             var margin = style['margin-left'];
26811             if (typeof(margin_to_depth[margin]) == 'undefined') {
26812                 max_margins++;
26813                 margin_to_depth[margin] = max_margins;
26814             }
26815             nlvl = margin_to_depth[margin] ;
26816              
26817             if (nlvl > lvl) {
26818                 //new indent
26819                 var nul = doc.createElement(listtype); // what about number lists...
26820                 if (!last_li) {
26821                     last_li = doc.createElement('li');
26822                     stack[lvl].appendChild(last_li);
26823                 }
26824                 last_li.appendChild(nul);
26825                 stack[nlvl] = nul;
26826                 
26827             }
26828             lvl = nlvl;
26829             
26830             // not starting at 1..
26831             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
26832                 stack[nlvl].setAttribute("start", num);
26833             }
26834             
26835             var nli = stack[nlvl].appendChild(doc.createElement('li'));
26836             last_li = nli;
26837             nli.innerHTML = n.innerHTML;
26838             //Roo.log("innerHTML = " + n.innerHTML);
26839             parent.removeChild(n);
26840             
26841              
26842              
26843             
26844         },this);
26845         
26846         
26847         
26848         
26849     }
26850     
26851     
26852     
26853 });
26854 /**
26855  * @class Roo.htmleditor.FilterStyleToTag
26856  * part of the word stuff... - certain 'styles' should be converted to tags.
26857  * eg.
26858  *   font-weight: bold -> bold
26859  *   ?? super / subscrit etc..
26860  * 
26861  * @constructor
26862 * Run a new style to tag filter.
26863 * @param {Object} config Configuration options
26864  */
26865 Roo.htmleditor.FilterStyleToTag = function(cfg)
26866 {
26867     
26868     this.tags = {
26869         B  : [ 'fontWeight' , 'bold'],
26870         I :  [ 'fontStyle' , 'italic'],
26871         //pre :  [ 'font-style' , 'italic'],
26872         // h1.. h6 ?? font-size?
26873         SUP : [ 'verticalAlign' , 'super' ],
26874         SUB : [ 'verticalAlign' , 'sub' ]
26875         
26876         
26877     };
26878     
26879     Roo.apply(this, cfg);
26880      
26881     
26882     this.walk(cfg.node);
26883     
26884     
26885     
26886 }
26887
26888
26889 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
26890 {
26891     tag: true, // all tags
26892     
26893     tags : false,
26894     
26895     
26896     replaceTag : function(node)
26897     {
26898         
26899         
26900         if (node.getAttribute("style") === null) {
26901             return true;
26902         }
26903         var inject = [];
26904         for (var k in this.tags) {
26905             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
26906                 inject.push(k);
26907                 node.style.removeProperty(this.tags[k][0]);
26908             }
26909         }
26910         if (!inject.length) {
26911             return true; 
26912         }
26913         var cn = Array.from(node.childNodes);
26914         var nn = node;
26915         Roo.each(inject, function(t) {
26916             var nc = node.ownerDocument.createElement(t);
26917             nn.appendChild(nc);
26918             nn = nc;
26919         });
26920         for(var i = 0;i < cn.length;cn++) {
26921             node.removeChild(cn[i]);
26922             nn.appendChild(cn[i]);
26923         }
26924         return true /// iterate thru
26925     }
26926     
26927 })/**
26928  * @class Roo.htmleditor.FilterLongBr
26929  * BR/BR/BR - keep a maximum of 2...
26930  * @constructor
26931  * Run a new Long BR Filter
26932  * @param {Object} config Configuration options
26933  */
26934
26935 Roo.htmleditor.FilterLongBr = function(cfg)
26936 {
26937     // no need to apply config.
26938     this.walk(cfg.node);
26939 }
26940
26941 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
26942 {
26943     
26944      
26945     tag : 'BR',
26946     
26947      
26948     replaceTag : function(node)
26949     {
26950         
26951         var ps = node.nextSibling;
26952         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26953             ps = ps.nextSibling;
26954         }
26955         
26956         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
26957             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
26958             return false;
26959         }
26960         
26961         if (!ps || ps.nodeType != 1) {
26962             return false;
26963         }
26964         
26965         if (!ps || ps.tagName != 'BR') {
26966            
26967             return false;
26968         }
26969         
26970         
26971         
26972         
26973         
26974         if (!node.previousSibling) {
26975             return false;
26976         }
26977         var ps = node.previousSibling;
26978         
26979         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26980             ps = ps.previousSibling;
26981         }
26982         if (!ps || ps.nodeType != 1) {
26983             return false;
26984         }
26985         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
26986         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
26987             return false;
26988         }
26989         
26990         node.parentNode.removeChild(node); // remove me...
26991         
26992         return false; // no need to do children
26993
26994     }
26995     
26996 }); 
26997
26998 /**
26999  * @class Roo.htmleditor.FilterBlock
27000  * removes id / data-block and contenteditable that are associated with blocks
27001  * usage should be done on a cloned copy of the dom
27002  * @constructor
27003 * Run a new Attribute Filter { node : xxxx }}
27004 * @param {Object} config Configuration options
27005  */
27006 Roo.htmleditor.FilterBlock = function(cfg)
27007 {
27008     Roo.apply(this, cfg);
27009     var qa = cfg.node.querySelectorAll;
27010     this.removeAttributes('data-block');
27011     this.removeAttributes('contenteditable');
27012     this.removeAttributes('id');
27013     
27014 }
27015
27016 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27017 {
27018     node: true, // all tags
27019      
27020      
27021     removeAttributes : function(attr)
27022     {
27023         var ar = this.node.querySelectorAll('*[' + attr + ']');
27024         for (var i =0;i<ar.length;i++) {
27025             ar[i].removeAttribute(attr);
27026         }
27027     }
27028         
27029         
27030         
27031     
27032 });
27033 /***
27034  * This is based loosely on tinymce 
27035  * @class Roo.htmleditor.TidySerializer
27036  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27037  * @constructor
27038  * @method Serializer
27039  * @param {Object} settings Name/value settings object.
27040  */
27041
27042
27043 Roo.htmleditor.TidySerializer = function(settings)
27044 {
27045     Roo.apply(this, settings);
27046     
27047     this.writer = new Roo.htmleditor.TidyWriter(settings);
27048     
27049     
27050
27051 };
27052 Roo.htmleditor.TidySerializer.prototype = {
27053     
27054     /**
27055      * @param {boolean} inner do the inner of the node.
27056      */
27057     inner : false,
27058     
27059     writer : false,
27060     
27061     /**
27062     * Serializes the specified node into a string.
27063     *
27064     * @example
27065     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27066     * @method serialize
27067     * @param {DomElement} node Node instance to serialize.
27068     * @return {String} String with HTML based on DOM tree.
27069     */
27070     serialize : function(node) {
27071         
27072         // = settings.validate;
27073         var writer = this.writer;
27074         var self  = this;
27075         this.handlers = {
27076             // #text
27077             3: function(node) {
27078                 
27079                 writer.text(node.nodeValue, node);
27080             },
27081             // #comment
27082             8: function(node) {
27083                 writer.comment(node.nodeValue);
27084             },
27085             // Processing instruction
27086             7: function(node) {
27087                 writer.pi(node.name, node.nodeValue);
27088             },
27089             // Doctype
27090             10: function(node) {
27091                 writer.doctype(node.nodeValue);
27092             },
27093             // CDATA
27094             4: function(node) {
27095                 writer.cdata(node.nodeValue);
27096             },
27097             // Document fragment
27098             11: function(node) {
27099                 node = node.firstChild;
27100                 if (!node) {
27101                     return;
27102                 }
27103                 while(node) {
27104                     self.walk(node);
27105                     node = node.nextSibling
27106                 }
27107             }
27108         };
27109         writer.reset();
27110         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27111         return writer.getContent();
27112     },
27113
27114     walk: function(node)
27115     {
27116         var attrName, attrValue, sortedAttrs, i, l, elementRule,
27117             handler = this.handlers[node.nodeType];
27118             
27119         if (handler) {
27120             handler(node);
27121             return;
27122         }
27123     
27124         var name = node.nodeName;
27125         var isEmpty = node.childNodes.length < 1;
27126       
27127         var writer = this.writer;
27128         var attrs = node.attributes;
27129         // Sort attributes
27130         
27131         writer.start(node.nodeName, attrs, isEmpty, node);
27132         if (isEmpty) {
27133             return;
27134         }
27135         node = node.firstChild;
27136         if (!node) {
27137             writer.end(name);
27138             return;
27139         }
27140         while (node) {
27141             this.walk(node);
27142             node = node.nextSibling;
27143         }
27144         writer.end(name);
27145         
27146     
27147     }
27148     // Serialize element and treat all non elements as fragments
27149    
27150 }; 
27151
27152 /***
27153  * This is based loosely on tinymce 
27154  * @class Roo.htmleditor.TidyWriter
27155  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27156  *
27157  * Known issues?
27158  * - not tested much with 'PRE' formated elements.
27159  * 
27160  *
27161  *
27162  */
27163
27164 Roo.htmleditor.TidyWriter = function(settings)
27165 {
27166     
27167     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27168     Roo.apply(this, settings);
27169     this.html = [];
27170     this.state = [];
27171      
27172     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27173   
27174 }
27175 Roo.htmleditor.TidyWriter.prototype = {
27176
27177  
27178     state : false,
27179     
27180     indent :  '  ',
27181     
27182     // part of state...
27183     indentstr : '',
27184     in_pre: false,
27185     in_inline : false,
27186     last_inline : false,
27187     encode : false,
27188      
27189     
27190             /**
27191     * Writes the a start element such as <p id="a">.
27192     *
27193     * @method start
27194     * @param {String} name Name of the element.
27195     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27196     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27197     */
27198     start: function(name, attrs, empty, node)
27199     {
27200         var i, l, attr, value;
27201         
27202         // there are some situations where adding line break && indentation will not work. will not work.
27203         // <span / b / i ... formating?
27204         
27205         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27206         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27207         
27208         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27209         
27210         var add_lb = name == 'BR' ? false : in_inline;
27211         
27212         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27213             i_inline = false;
27214         }
27215
27216         var indentstr =  this.indentstr;
27217         
27218         // e_inline = elements that can be inline, but still allow \n before and after?
27219         // only 'BR' ??? any others?
27220         
27221         // ADD LINE BEFORE tage
27222         if (!this.in_pre) {
27223             if (in_inline) {
27224                 //code
27225                 if (name == 'BR') {
27226                     this.addLine();
27227                 } else if (this.lastElementEndsWS()) {
27228                     this.addLine();
27229                 } else{
27230                     // otherwise - no new line. (and dont indent.)
27231                     indentstr = '';
27232                 }
27233                 
27234             } else {
27235                 this.addLine();
27236             }
27237         } else {
27238             indentstr = '';
27239         }
27240         
27241         this.html.push(indentstr + '<', name.toLowerCase());
27242         
27243         if (attrs) {
27244             for (i = 0, l = attrs.length; i < l; i++) {
27245                 attr = attrs[i];
27246                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27247             }
27248         }
27249      
27250         if (empty) {
27251             if (is_short) {
27252                 this.html[this.html.length] = '/>';
27253             } else {
27254                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27255             }
27256             var e_inline = name == 'BR' ? false : this.in_inline;
27257             
27258             if (!e_inline && !this.in_pre) {
27259                 this.addLine();
27260             }
27261             return;
27262         
27263         }
27264         // not empty..
27265         this.html[this.html.length] = '>';
27266         
27267         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27268         /*
27269         if (!in_inline && !in_pre) {
27270             var cn = node.firstChild;
27271             while(cn) {
27272                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27273                     in_inline = true
27274                     break;
27275                 }
27276                 cn = cn.nextSibling;
27277             }
27278              
27279         }
27280         */
27281         
27282         
27283         this.pushState({
27284             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
27285             in_pre : in_pre,
27286             in_inline :  in_inline
27287         });
27288         // add a line after if we are not in a
27289         
27290         if (!in_inline && !in_pre) {
27291             this.addLine();
27292         }
27293         
27294             
27295          
27296         
27297     },
27298     
27299     lastElementEndsWS : function()
27300     {
27301         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27302         if (value === false) {
27303             return true;
27304         }
27305         return value.match(/\s+$/);
27306         
27307     },
27308     
27309     /**
27310      * Writes the a end element such as </p>.
27311      *
27312      * @method end
27313      * @param {String} name Name of the element.
27314      */
27315     end: function(name) {
27316         var value;
27317         this.popState();
27318         var indentstr = '';
27319         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27320         
27321         if (!this.in_pre && !in_inline) {
27322             this.addLine();
27323             indentstr  = this.indentstr;
27324         }
27325         this.html.push(indentstr + '</', name.toLowerCase(), '>');
27326         this.last_inline = in_inline;
27327         
27328         // pop the indent state..
27329     },
27330     /**
27331      * Writes a text node.
27332      *
27333      * In pre - we should not mess with the contents.
27334      * 
27335      *
27336      * @method text
27337      * @param {String} text String to write out.
27338      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27339      */
27340     text: function(in_text, node)
27341     {
27342         // if not in whitespace critical
27343         if (in_text.length < 1) {
27344             return;
27345         }
27346         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27347         
27348         if (this.in_pre) {
27349             this.html[this.html.length] =  text;
27350             return;   
27351         }
27352         
27353         if (this.in_inline) {
27354             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27355             if (text != ' ') {
27356                 text = text.replace(/\s+/,' ');  // all white space to single white space
27357                 
27358                     
27359                 // if next tag is '<BR>', then we can trim right..
27360                 if (node.nextSibling &&
27361                     node.nextSibling.nodeType == 1 &&
27362                     node.nextSibling.nodeName == 'BR' )
27363                 {
27364                     text = text.replace(/\s+$/g,'');
27365                 }
27366                 // if previous tag was a BR, we can also trim..
27367                 if (node.previousSibling &&
27368                     node.previousSibling.nodeType == 1 &&
27369                     node.previousSibling.nodeName == 'BR' )
27370                 {
27371                     text = this.indentstr +  text.replace(/^\s+/g,'');
27372                 }
27373                 if (text.match(/\n/)) {
27374                     text = text.replace(
27375                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27376                     );
27377                     // remoeve the last whitespace / line break.
27378                     text = text.replace(/\n\s+$/,'');
27379                 }
27380                 // repace long lines
27381                 
27382             }
27383              
27384             this.html[this.html.length] =  text;
27385             return;   
27386         }
27387         // see if previous element was a inline element.
27388         var indentstr = this.indentstr;
27389    
27390         text = text.replace(/\s+/g," "); // all whitespace into single white space.
27391         
27392         // should trim left?
27393         if (node.previousSibling &&
27394             node.previousSibling.nodeType == 1 &&
27395             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27396         {
27397             indentstr = '';
27398             
27399         } else {
27400             this.addLine();
27401             text = text.replace(/^\s+/,''); // trim left
27402           
27403         }
27404         // should trim right?
27405         if (node.nextSibling &&
27406             node.nextSibling.nodeType == 1 &&
27407             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27408         {
27409           // noop
27410             
27411         }  else {
27412             text = text.replace(/\s+$/,''); // trim right
27413         }
27414          
27415               
27416         
27417         
27418         
27419         if (text.length < 1) {
27420             return;
27421         }
27422         if (!text.match(/\n/)) {
27423             this.html.push(indentstr + text);
27424             return;
27425         }
27426         
27427         text = this.indentstr + text.replace(
27428             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27429         );
27430         // remoeve the last whitespace / line break.
27431         text = text.replace(/\s+$/,''); 
27432         
27433         this.html.push(text);
27434         
27435         // split and indent..
27436         
27437         
27438     },
27439     /**
27440      * Writes a cdata node such as <![CDATA[data]]>.
27441      *
27442      * @method cdata
27443      * @param {String} text String to write out inside the cdata.
27444      */
27445     cdata: function(text) {
27446         this.html.push('<![CDATA[', text, ']]>');
27447     },
27448     /**
27449     * Writes a comment node such as <!-- Comment -->.
27450     *
27451     * @method cdata
27452     * @param {String} text String to write out inside the comment.
27453     */
27454    comment: function(text) {
27455        this.html.push('<!--', text, '-->');
27456    },
27457     /**
27458      * Writes a PI node such as <?xml attr="value" ?>.
27459      *
27460      * @method pi
27461      * @param {String} name Name of the pi.
27462      * @param {String} text String to write out inside the pi.
27463      */
27464     pi: function(name, text) {
27465         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
27466         this.indent != '' && this.html.push('\n');
27467     },
27468     /**
27469      * Writes a doctype node such as <!DOCTYPE data>.
27470      *
27471      * @method doctype
27472      * @param {String} text String to write out inside the doctype.
27473      */
27474     doctype: function(text) {
27475         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
27476     },
27477     /**
27478      * Resets the internal buffer if one wants to reuse the writer.
27479      *
27480      * @method reset
27481      */
27482     reset: function() {
27483         this.html.length = 0;
27484         this.state = [];
27485         this.pushState({
27486             indentstr : '',
27487             in_pre : false, 
27488             in_inline : false
27489         })
27490     },
27491     /**
27492      * Returns the contents that got serialized.
27493      *
27494      * @method getContent
27495      * @return {String} HTML contents that got written down.
27496      */
27497     getContent: function() {
27498         return this.html.join('').replace(/\n$/, '');
27499     },
27500     
27501     pushState : function(cfg)
27502     {
27503         this.state.push(cfg);
27504         Roo.apply(this, cfg);
27505     },
27506     
27507     popState : function()
27508     {
27509         if (this.state.length < 1) {
27510             return; // nothing to push
27511         }
27512         var cfg = {
27513             in_pre: false,
27514             indentstr : ''
27515         };
27516         this.state.pop();
27517         if (this.state.length > 0) {
27518             cfg = this.state[this.state.length-1]; 
27519         }
27520         Roo.apply(this, cfg);
27521     },
27522     
27523     addLine: function()
27524     {
27525         if (this.html.length < 1) {
27526             return;
27527         }
27528         
27529         
27530         var value = this.html[this.html.length - 1];
27531         if (value.length > 0 && '\n' !== value) {
27532             this.html.push('\n');
27533         }
27534     }
27535     
27536     
27537 //'pre script noscript style textarea video audio iframe object code'
27538 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
27539 // inline 
27540 };
27541
27542 Roo.htmleditor.TidyWriter.inline_elements = [
27543         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
27544         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
27545 ];
27546 Roo.htmleditor.TidyWriter.shortend_elements = [
27547     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
27548     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
27549 ];
27550
27551 Roo.htmleditor.TidyWriter.whitespace_elements = [
27552     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
27553 ];/***
27554  * This is based loosely on tinymce 
27555  * @class Roo.htmleditor.TidyEntities
27556  * @static
27557  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27558  *
27559  * Not 100% sure this is actually used or needed.
27560  */
27561
27562 Roo.htmleditor.TidyEntities = {
27563     
27564     /**
27565      * initialize data..
27566      */
27567     init : function (){
27568      
27569         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
27570        
27571     },
27572
27573
27574     buildEntitiesLookup: function(items, radix) {
27575         var i, chr, entity, lookup = {};
27576         if (!items) {
27577             return {};
27578         }
27579         items = typeof(items) == 'string' ? items.split(',') : items;
27580         radix = radix || 10;
27581         // Build entities lookup table
27582         for (i = 0; i < items.length; i += 2) {
27583             chr = String.fromCharCode(parseInt(items[i], radix));
27584             // Only add non base entities
27585             if (!this.baseEntities[chr]) {
27586                 entity = '&' + items[i + 1] + ';';
27587                 lookup[chr] = entity;
27588                 lookup[entity] = chr;
27589             }
27590         }
27591         return lookup;
27592         
27593     },
27594     
27595     asciiMap : {
27596             128: '€',
27597             130: '‚',
27598             131: 'ƒ',
27599             132: '„',
27600             133: '…',
27601             134: '†',
27602             135: '‡',
27603             136: 'ˆ',
27604             137: '‰',
27605             138: 'Š',
27606             139: '‹',
27607             140: 'Œ',
27608             142: 'Ž',
27609             145: '‘',
27610             146: '’',
27611             147: '“',
27612             148: '”',
27613             149: '•',
27614             150: '–',
27615             151: '—',
27616             152: '˜',
27617             153: '™',
27618             154: 'š',
27619             155: '›',
27620             156: 'œ',
27621             158: 'ž',
27622             159: 'Ÿ'
27623     },
27624     // Raw entities
27625     baseEntities : {
27626         '"': '&quot;',
27627         // Needs to be escaped since the YUI compressor would otherwise break the code
27628         '\'': '&#39;',
27629         '<': '&lt;',
27630         '>': '&gt;',
27631         '&': '&amp;',
27632         '`': '&#96;'
27633     },
27634     // Reverse lookup table for raw entities
27635     reverseEntities : {
27636         '&lt;': '<',
27637         '&gt;': '>',
27638         '&amp;': '&',
27639         '&quot;': '"',
27640         '&apos;': '\''
27641     },
27642     
27643     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
27644     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
27645     rawCharsRegExp : /[<>&\"\']/g,
27646     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
27647     namedEntities  : false,
27648     namedEntitiesData : [ 
27649         '50',
27650         'nbsp',
27651         '51',
27652         'iexcl',
27653         '52',
27654         'cent',
27655         '53',
27656         'pound',
27657         '54',
27658         'curren',
27659         '55',
27660         'yen',
27661         '56',
27662         'brvbar',
27663         '57',
27664         'sect',
27665         '58',
27666         'uml',
27667         '59',
27668         'copy',
27669         '5a',
27670         'ordf',
27671         '5b',
27672         'laquo',
27673         '5c',
27674         'not',
27675         '5d',
27676         'shy',
27677         '5e',
27678         'reg',
27679         '5f',
27680         'macr',
27681         '5g',
27682         'deg',
27683         '5h',
27684         'plusmn',
27685         '5i',
27686         'sup2',
27687         '5j',
27688         'sup3',
27689         '5k',
27690         'acute',
27691         '5l',
27692         'micro',
27693         '5m',
27694         'para',
27695         '5n',
27696         'middot',
27697         '5o',
27698         'cedil',
27699         '5p',
27700         'sup1',
27701         '5q',
27702         'ordm',
27703         '5r',
27704         'raquo',
27705         '5s',
27706         'frac14',
27707         '5t',
27708         'frac12',
27709         '5u',
27710         'frac34',
27711         '5v',
27712         'iquest',
27713         '60',
27714         'Agrave',
27715         '61',
27716         'Aacute',
27717         '62',
27718         'Acirc',
27719         '63',
27720         'Atilde',
27721         '64',
27722         'Auml',
27723         '65',
27724         'Aring',
27725         '66',
27726         'AElig',
27727         '67',
27728         'Ccedil',
27729         '68',
27730         'Egrave',
27731         '69',
27732         'Eacute',
27733         '6a',
27734         'Ecirc',
27735         '6b',
27736         'Euml',
27737         '6c',
27738         'Igrave',
27739         '6d',
27740         'Iacute',
27741         '6e',
27742         'Icirc',
27743         '6f',
27744         'Iuml',
27745         '6g',
27746         'ETH',
27747         '6h',
27748         'Ntilde',
27749         '6i',
27750         'Ograve',
27751         '6j',
27752         'Oacute',
27753         '6k',
27754         'Ocirc',
27755         '6l',
27756         'Otilde',
27757         '6m',
27758         'Ouml',
27759         '6n',
27760         'times',
27761         '6o',
27762         'Oslash',
27763         '6p',
27764         'Ugrave',
27765         '6q',
27766         'Uacute',
27767         '6r',
27768         'Ucirc',
27769         '6s',
27770         'Uuml',
27771         '6t',
27772         'Yacute',
27773         '6u',
27774         'THORN',
27775         '6v',
27776         'szlig',
27777         '70',
27778         'agrave',
27779         '71',
27780         'aacute',
27781         '72',
27782         'acirc',
27783         '73',
27784         'atilde',
27785         '74',
27786         'auml',
27787         '75',
27788         'aring',
27789         '76',
27790         'aelig',
27791         '77',
27792         'ccedil',
27793         '78',
27794         'egrave',
27795         '79',
27796         'eacute',
27797         '7a',
27798         'ecirc',
27799         '7b',
27800         'euml',
27801         '7c',
27802         'igrave',
27803         '7d',
27804         'iacute',
27805         '7e',
27806         'icirc',
27807         '7f',
27808         'iuml',
27809         '7g',
27810         'eth',
27811         '7h',
27812         'ntilde',
27813         '7i',
27814         'ograve',
27815         '7j',
27816         'oacute',
27817         '7k',
27818         'ocirc',
27819         '7l',
27820         'otilde',
27821         '7m',
27822         'ouml',
27823         '7n',
27824         'divide',
27825         '7o',
27826         'oslash',
27827         '7p',
27828         'ugrave',
27829         '7q',
27830         'uacute',
27831         '7r',
27832         'ucirc',
27833         '7s',
27834         'uuml',
27835         '7t',
27836         'yacute',
27837         '7u',
27838         'thorn',
27839         '7v',
27840         'yuml',
27841         'ci',
27842         'fnof',
27843         'sh',
27844         'Alpha',
27845         'si',
27846         'Beta',
27847         'sj',
27848         'Gamma',
27849         'sk',
27850         'Delta',
27851         'sl',
27852         'Epsilon',
27853         'sm',
27854         'Zeta',
27855         'sn',
27856         'Eta',
27857         'so',
27858         'Theta',
27859         'sp',
27860         'Iota',
27861         'sq',
27862         'Kappa',
27863         'sr',
27864         'Lambda',
27865         'ss',
27866         'Mu',
27867         'st',
27868         'Nu',
27869         'su',
27870         'Xi',
27871         'sv',
27872         'Omicron',
27873         't0',
27874         'Pi',
27875         't1',
27876         'Rho',
27877         't3',
27878         'Sigma',
27879         't4',
27880         'Tau',
27881         't5',
27882         'Upsilon',
27883         't6',
27884         'Phi',
27885         't7',
27886         'Chi',
27887         't8',
27888         'Psi',
27889         't9',
27890         'Omega',
27891         'th',
27892         'alpha',
27893         'ti',
27894         'beta',
27895         'tj',
27896         'gamma',
27897         'tk',
27898         'delta',
27899         'tl',
27900         'epsilon',
27901         'tm',
27902         'zeta',
27903         'tn',
27904         'eta',
27905         'to',
27906         'theta',
27907         'tp',
27908         'iota',
27909         'tq',
27910         'kappa',
27911         'tr',
27912         'lambda',
27913         'ts',
27914         'mu',
27915         'tt',
27916         'nu',
27917         'tu',
27918         'xi',
27919         'tv',
27920         'omicron',
27921         'u0',
27922         'pi',
27923         'u1',
27924         'rho',
27925         'u2',
27926         'sigmaf',
27927         'u3',
27928         'sigma',
27929         'u4',
27930         'tau',
27931         'u5',
27932         'upsilon',
27933         'u6',
27934         'phi',
27935         'u7',
27936         'chi',
27937         'u8',
27938         'psi',
27939         'u9',
27940         'omega',
27941         'uh',
27942         'thetasym',
27943         'ui',
27944         'upsih',
27945         'um',
27946         'piv',
27947         '812',
27948         'bull',
27949         '816',
27950         'hellip',
27951         '81i',
27952         'prime',
27953         '81j',
27954         'Prime',
27955         '81u',
27956         'oline',
27957         '824',
27958         'frasl',
27959         '88o',
27960         'weierp',
27961         '88h',
27962         'image',
27963         '88s',
27964         'real',
27965         '892',
27966         'trade',
27967         '89l',
27968         'alefsym',
27969         '8cg',
27970         'larr',
27971         '8ch',
27972         'uarr',
27973         '8ci',
27974         'rarr',
27975         '8cj',
27976         'darr',
27977         '8ck',
27978         'harr',
27979         '8dl',
27980         'crarr',
27981         '8eg',
27982         'lArr',
27983         '8eh',
27984         'uArr',
27985         '8ei',
27986         'rArr',
27987         '8ej',
27988         'dArr',
27989         '8ek',
27990         'hArr',
27991         '8g0',
27992         'forall',
27993         '8g2',
27994         'part',
27995         '8g3',
27996         'exist',
27997         '8g5',
27998         'empty',
27999         '8g7',
28000         'nabla',
28001         '8g8',
28002         'isin',
28003         '8g9',
28004         'notin',
28005         '8gb',
28006         'ni',
28007         '8gf',
28008         'prod',
28009         '8gh',
28010         'sum',
28011         '8gi',
28012         'minus',
28013         '8gn',
28014         'lowast',
28015         '8gq',
28016         'radic',
28017         '8gt',
28018         'prop',
28019         '8gu',
28020         'infin',
28021         '8h0',
28022         'ang',
28023         '8h7',
28024         'and',
28025         '8h8',
28026         'or',
28027         '8h9',
28028         'cap',
28029         '8ha',
28030         'cup',
28031         '8hb',
28032         'int',
28033         '8hk',
28034         'there4',
28035         '8hs',
28036         'sim',
28037         '8i5',
28038         'cong',
28039         '8i8',
28040         'asymp',
28041         '8j0',
28042         'ne',
28043         '8j1',
28044         'equiv',
28045         '8j4',
28046         'le',
28047         '8j5',
28048         'ge',
28049         '8k2',
28050         'sub',
28051         '8k3',
28052         'sup',
28053         '8k4',
28054         'nsub',
28055         '8k6',
28056         'sube',
28057         '8k7',
28058         'supe',
28059         '8kl',
28060         'oplus',
28061         '8kn',
28062         'otimes',
28063         '8l5',
28064         'perp',
28065         '8m5',
28066         'sdot',
28067         '8o8',
28068         'lceil',
28069         '8o9',
28070         'rceil',
28071         '8oa',
28072         'lfloor',
28073         '8ob',
28074         'rfloor',
28075         '8p9',
28076         'lang',
28077         '8pa',
28078         'rang',
28079         '9ea',
28080         'loz',
28081         '9j0',
28082         'spades',
28083         '9j3',
28084         'clubs',
28085         '9j5',
28086         'hearts',
28087         '9j6',
28088         'diams',
28089         'ai',
28090         'OElig',
28091         'aj',
28092         'oelig',
28093         'b0',
28094         'Scaron',
28095         'b1',
28096         'scaron',
28097         'bo',
28098         'Yuml',
28099         'm6',
28100         'circ',
28101         'ms',
28102         'tilde',
28103         '802',
28104         'ensp',
28105         '803',
28106         'emsp',
28107         '809',
28108         'thinsp',
28109         '80c',
28110         'zwnj',
28111         '80d',
28112         'zwj',
28113         '80e',
28114         'lrm',
28115         '80f',
28116         'rlm',
28117         '80j',
28118         'ndash',
28119         '80k',
28120         'mdash',
28121         '80o',
28122         'lsquo',
28123         '80p',
28124         'rsquo',
28125         '80q',
28126         'sbquo',
28127         '80s',
28128         'ldquo',
28129         '80t',
28130         'rdquo',
28131         '80u',
28132         'bdquo',
28133         '810',
28134         'dagger',
28135         '811',
28136         'Dagger',
28137         '81g',
28138         'permil',
28139         '81p',
28140         'lsaquo',
28141         '81q',
28142         'rsaquo',
28143         '85c',
28144         'euro'
28145     ],
28146
28147          
28148     /**
28149      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28150      *
28151      * @method encodeRaw
28152      * @param {String} text Text to encode.
28153      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28154      * @return {String} Entity encoded text.
28155      */
28156     encodeRaw: function(text, attr)
28157     {
28158         var t = this;
28159         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28160             return t.baseEntities[chr] || chr;
28161         });
28162     },
28163     /**
28164      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28165      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28166      * and is exposed as the DOMUtils.encode function.
28167      *
28168      * @method encodeAllRaw
28169      * @param {String} text Text to encode.
28170      * @return {String} Entity encoded text.
28171      */
28172     encodeAllRaw: function(text) {
28173         var t = this;
28174         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28175             return t.baseEntities[chr] || chr;
28176         });
28177     },
28178     /**
28179      * Encodes the specified string using numeric entities. The core entities will be
28180      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28181      *
28182      * @method encodeNumeric
28183      * @param {String} text Text to encode.
28184      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28185      * @return {String} Entity encoded text.
28186      */
28187     encodeNumeric: function(text, attr) {
28188         var t = this;
28189         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28190             // Multi byte sequence convert it to a single entity
28191             if (chr.length > 1) {
28192                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28193             }
28194             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28195         });
28196     },
28197     /**
28198      * Encodes the specified string using named entities. The core entities will be encoded
28199      * as named ones but all non lower ascii characters will be encoded into named entities.
28200      *
28201      * @method encodeNamed
28202      * @param {String} text Text to encode.
28203      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28204      * @param {Object} entities Optional parameter with entities to use.
28205      * @return {String} Entity encoded text.
28206      */
28207     encodeNamed: function(text, attr, entities) {
28208         var t = this;
28209         entities = entities || this.namedEntities;
28210         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28211             return t.baseEntities[chr] || entities[chr] || chr;
28212         });
28213     },
28214     /**
28215      * Returns an encode function based on the name(s) and it's optional entities.
28216      *
28217      * @method getEncodeFunc
28218      * @param {String} name Comma separated list of encoders for example named,numeric.
28219      * @param {String} entities Optional parameter with entities to use instead of the built in set.
28220      * @return {function} Encode function to be used.
28221      */
28222     getEncodeFunc: function(name, entities) {
28223         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28224         var t = this;
28225         function encodeNamedAndNumeric(text, attr) {
28226             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28227                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28228             });
28229         }
28230
28231         function encodeCustomNamed(text, attr) {
28232             return t.encodeNamed(text, attr, entities);
28233         }
28234         // Replace + with , to be compatible with previous TinyMCE versions
28235         name = this.makeMap(name.replace(/\+/g, ','));
28236         // Named and numeric encoder
28237         if (name.named && name.numeric) {
28238             return this.encodeNamedAndNumeric;
28239         }
28240         // Named encoder
28241         if (name.named) {
28242             // Custom names
28243             if (entities) {
28244                 return encodeCustomNamed;
28245             }
28246             return this.encodeNamed;
28247         }
28248         // Numeric
28249         if (name.numeric) {
28250             return this.encodeNumeric;
28251         }
28252         // Raw encoder
28253         return this.encodeRaw;
28254     },
28255     /**
28256      * Decodes the specified string, this will replace entities with raw UTF characters.
28257      *
28258      * @method decode
28259      * @param {String} text Text to entity decode.
28260      * @return {String} Entity decoded string.
28261      */
28262     decode: function(text)
28263     {
28264         var  t = this;
28265         return text.replace(this.entityRegExp, function(all, numeric) {
28266             if (numeric) {
28267                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28268                 // Support upper UTF
28269                 if (numeric > 65535) {
28270                     numeric -= 65536;
28271                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28272                 }
28273                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28274             }
28275             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28276         });
28277     },
28278     nativeDecode : function (text) {
28279         return text;
28280     },
28281     makeMap : function (items, delim, map) {
28282                 var i;
28283                 items = items || [];
28284                 delim = delim || ',';
28285                 if (typeof items == "string") {
28286                         items = items.split(delim);
28287                 }
28288                 map = map || {};
28289                 i = items.length;
28290                 while (i--) {
28291                         map[items[i]] = {};
28292                 }
28293                 return map;
28294         }
28295 };
28296     
28297     
28298     
28299 Roo.htmleditor.TidyEntities.init();
28300 /**
28301  * @class Roo.htmleditor.KeyEnter
28302  * Handle Enter press..
28303  * @cfg {Roo.HtmlEditorCore} core the editor.
28304  * @constructor
28305  * Create a new Filter.
28306  * @param {Object} config Configuration options
28307  */
28308
28309
28310
28311
28312
28313 Roo.htmleditor.KeyEnter = function(cfg) {
28314     Roo.apply(this, cfg);
28315     // this does not actually call walk as it's really just a abstract class
28316  
28317     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28318 }
28319
28320 //Roo.htmleditor.KeyEnter.i = 0;
28321
28322
28323 Roo.htmleditor.KeyEnter.prototype = {
28324     
28325     core : false,
28326     
28327     keypress : function(e)
28328     {
28329         if (e.charCode != 13 && e.charCode != 10) {
28330             Roo.log([e.charCode,e]);
28331             return true;
28332         }
28333         e.preventDefault();
28334         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28335         var doc = this.core.doc;
28336           //add a new line
28337        
28338     
28339         var sel = this.core.getSelection();
28340         var range = sel.getRangeAt(0);
28341         var n = range.commonAncestorContainer;
28342         var pc = range.closest([ 'ol', 'ul']);
28343         var pli = range.closest('li');
28344         if (!pc || e.ctrlKey) {
28345             // on it list, or ctrl pressed.
28346             if (!e.ctrlKey) {
28347                 sel.insertNode('br', 'after'); 
28348             } else {
28349                 // only do this if we have ctrl key..
28350                 var br = doc.createElement('br');
28351                 br.className = 'clear';
28352                 br.setAttribute('style', 'clear: both');
28353                 sel.insertNode(br, 'after'); 
28354             }
28355             
28356          
28357             this.core.undoManager.addEvent();
28358             this.core.fireEditorEvent(e);
28359             return false;
28360         }
28361         
28362         // deal with <li> insetion
28363         if (pli.innerText.trim() == '' &&
28364             pli.previousSibling &&
28365             pli.previousSibling.nodeName == 'LI' &&
28366             pli.previousSibling.innerText.trim() ==  '') {
28367             pli.parentNode.removeChild(pli.previousSibling);
28368             sel.cursorAfter(pc);
28369             this.core.undoManager.addEvent();
28370             this.core.fireEditorEvent(e);
28371             return false;
28372         }
28373     
28374         var li = doc.createElement('LI');
28375         li.innerHTML = '&nbsp;';
28376         if (!pli || !pli.firstSibling) {
28377             pc.appendChild(li);
28378         } else {
28379             pli.parentNode.insertBefore(li, pli.firstSibling);
28380         }
28381         sel.cursorText (li.firstChild);
28382       
28383         this.core.undoManager.addEvent();
28384         this.core.fireEditorEvent(e);
28385
28386         return false;
28387         
28388     
28389         
28390         
28391          
28392     }
28393 };
28394      
28395 /**
28396  * @class Roo.htmleditor.Block
28397  * Base class for html editor blocks - do not use it directly .. extend it..
28398  * @cfg {DomElement} node The node to apply stuff to.
28399  * @cfg {String} friendly_name the name that appears in the context bar about this block
28400  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28401  
28402  * @constructor
28403  * Create a new Filter.
28404  * @param {Object} config Configuration options
28405  */
28406
28407 Roo.htmleditor.Block  = function(cfg)
28408 {
28409     // do nothing .. should not be called really.
28410 }
28411 /**
28412  * factory method to get the block from an element (using cache if necessary)
28413  * @static
28414  * @param {HtmlElement} the dom element
28415  */
28416 Roo.htmleditor.Block.factory = function(node)
28417 {
28418     var cc = Roo.htmleditor.Block.cache;
28419     var id = Roo.get(node).id;
28420     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28421         Roo.htmleditor.Block.cache[id].readElement(node);
28422         return Roo.htmleditor.Block.cache[id];
28423     }
28424     var db  = node.getAttribute('data-block');
28425     if (!db) {
28426         db = node.nodeName.toLowerCase().toUpperCaseFirst();
28427     }
28428     var cls = Roo.htmleditor['Block' + db];
28429     if (typeof(cls) == 'undefined') {
28430         //Roo.log(node.getAttribute('data-block'));
28431         Roo.log("OOps missing block : " + 'Block' + db);
28432         return false;
28433     }
28434     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
28435     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
28436 };
28437
28438 /**
28439  * initalize all Elements from content that are 'blockable'
28440  * @static
28441  * @param the body element
28442  */
28443 Roo.htmleditor.Block.initAll = function(body, type)
28444 {
28445     if (typeof(type) == 'undefined') {
28446         var ia = Roo.htmleditor.Block.initAll;
28447         ia(body,'table');
28448         ia(body,'td');
28449         ia(body,'figure');
28450         return;
28451     }
28452     Roo.each(Roo.get(body).query(type), function(e) {
28453         Roo.htmleditor.Block.factory(e);    
28454     },this);
28455 };
28456 // question goes here... do we need to clear out this cache sometimes?
28457 // or show we make it relivant to the htmleditor.
28458 Roo.htmleditor.Block.cache = {};
28459
28460 Roo.htmleditor.Block.prototype = {
28461     
28462     node : false,
28463     
28464      // used by context menu
28465     friendly_name : 'Based Block',
28466     
28467     // text for button to delete this element
28468     deleteTitle : false,
28469     
28470     context : false,
28471     /**
28472      * Update a node with values from this object
28473      * @param {DomElement} node
28474      */
28475     updateElement : function(node)
28476     {
28477         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
28478     },
28479      /**
28480      * convert to plain HTML for calling insertAtCursor..
28481      */
28482     toHTML : function()
28483     {
28484         return Roo.DomHelper.markup(this.toObject());
28485     },
28486     /**
28487      * used by readEleemnt to extract data from a node
28488      * may need improving as it's pretty basic
28489      
28490      * @param {DomElement} node
28491      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
28492      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
28493      * @param {String} style the style property - eg. text-align
28494      */
28495     getVal : function(node, tag, attr, style)
28496     {
28497         var n = node;
28498         if (tag !== true && n.tagName != tag.toUpperCase()) {
28499             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
28500             // but kiss for now.
28501             n = node.getElementsByTagName(tag).item(0);
28502         }
28503         if (!n) {
28504             return '';
28505         }
28506         if (attr === false) {
28507             return n;
28508         }
28509         if (attr == 'html') {
28510             return n.innerHTML;
28511         }
28512         if (attr == 'style') {
28513             return n.style[style]; 
28514         }
28515         
28516         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
28517             
28518     },
28519     /**
28520      * create a DomHelper friendly object - for use with 
28521      * Roo.DomHelper.markup / overwrite / etc..
28522      * (override this)
28523      */
28524     toObject : function()
28525     {
28526         return {};
28527     },
28528       /**
28529      * Read a node that has a 'data-block' property - and extract the values from it.
28530      * @param {DomElement} node - the node
28531      */
28532     readElement : function(node)
28533     {
28534         
28535     } 
28536     
28537     
28538 };
28539
28540  
28541
28542 /**
28543  * @class Roo.htmleditor.BlockFigure
28544  * Block that has an image and a figcaption
28545  * @cfg {String} image_src the url for the image
28546  * @cfg {String} align (left|right) alignment for the block default left
28547  * @cfg {String} caption the text to appear below  (and in the alt tag)
28548  * @cfg {String} caption_display (block|none) display or not the caption
28549  * @cfg {String|number} image_width the width of the image number or %?
28550  * @cfg {String|number} image_height the height of the image number or %?
28551  * 
28552  * @constructor
28553  * Create a new Filter.
28554  * @param {Object} config Configuration options
28555  */
28556
28557 Roo.htmleditor.BlockFigure = function(cfg)
28558 {
28559     if (cfg.node) {
28560         this.readElement(cfg.node);
28561         this.updateElement(cfg.node);
28562     }
28563     Roo.apply(this, cfg);
28564 }
28565 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
28566  
28567     
28568     // setable values.
28569     image_src: '',
28570     align: 'center',
28571     caption : '',
28572     caption_display : 'block',
28573     width : '100%',
28574     cls : '',
28575     href: '',
28576     video_url : '',
28577     
28578     // margin: '2%', not used
28579     
28580     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
28581
28582     
28583     // used by context menu
28584     friendly_name : 'Image with caption',
28585     deleteTitle : "Delete Image and Caption",
28586     
28587     contextMenu : function(toolbar)
28588     {
28589         
28590         var block = function() {
28591             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
28592         };
28593         
28594         
28595         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
28596         
28597         var syncValue = toolbar.editorcore.syncValue;
28598         
28599         var fields = {};
28600         
28601         return [
28602              {
28603                 xtype : 'TextItem',
28604                 text : "Source: ",
28605                 xns : rooui.Toolbar  //Boostrap?
28606             },
28607             {
28608                 xtype : 'Button',
28609                 text: 'Change Image URL',
28610                  
28611                 listeners : {
28612                     click: function (btn, state)
28613                     {
28614                         var b = block();
28615                         
28616                         Roo.MessageBox.show({
28617                             title : "Image Source URL",
28618                             msg : "Enter the url for the image",
28619                             buttons: Roo.MessageBox.OKCANCEL,
28620                             fn: function(btn, val){
28621                                 if (btn != 'ok') {
28622                                     return;
28623                                 }
28624                                 b.image_src = val;
28625                                 b.updateElement();
28626                                 syncValue();
28627                                 toolbar.editorcore.onEditorEvent();
28628                             },
28629                             minWidth:250,
28630                             prompt:true,
28631                             //multiline: multiline,
28632                             modal : true,
28633                             value : b.image_src
28634                         });
28635                     }
28636                 },
28637                 xns : rooui.Toolbar
28638             },
28639          
28640             {
28641                 xtype : 'Button',
28642                 text: 'Change Link URL',
28643                  
28644                 listeners : {
28645                     click: function (btn, state)
28646                     {
28647                         var b = block();
28648                         
28649                         Roo.MessageBox.show({
28650                             title : "Link URL",
28651                             msg : "Enter the url for the link - leave blank to have no link",
28652                             buttons: Roo.MessageBox.OKCANCEL,
28653                             fn: function(btn, val){
28654                                 if (btn != 'ok') {
28655                                     return;
28656                                 }
28657                                 b.href = val;
28658                                 b.updateElement();
28659                                 syncValue();
28660                                 toolbar.editorcore.onEditorEvent();
28661                             },
28662                             minWidth:250,
28663                             prompt:true,
28664                             //multiline: multiline,
28665                             modal : true,
28666                             value : b.href
28667                         });
28668                     }
28669                 },
28670                 xns : rooui.Toolbar
28671             },
28672             {
28673                 xtype : 'Button',
28674                 text: 'Show Video URL',
28675                  
28676                 listeners : {
28677                     click: function (btn, state)
28678                     {
28679                         Roo.MessageBox.alert("Video URL",
28680                             block().video_url == '' ? 'This image is not linked ot a video' :
28681                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
28682                     }
28683                 },
28684                 xns : rooui.Toolbar
28685             },
28686             
28687             
28688             {
28689                 xtype : 'TextItem',
28690                 text : "Width: ",
28691                 xns : rooui.Toolbar  //Boostrap?
28692             },
28693             {
28694                 xtype : 'ComboBox',
28695                 allowBlank : false,
28696                 displayField : 'val',
28697                 editable : true,
28698                 listWidth : 100,
28699                 triggerAction : 'all',
28700                 typeAhead : true,
28701                 valueField : 'val',
28702                 width : 70,
28703                 name : 'width',
28704                 listeners : {
28705                     select : function (combo, r, index)
28706                     {
28707                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28708                         var b = block();
28709                         b.width = r.get('val');
28710                         b.updateElement();
28711                         syncValue();
28712                         toolbar.editorcore.onEditorEvent();
28713                     }
28714                 },
28715                 xns : rooui.form,
28716                 store : {
28717                     xtype : 'SimpleStore',
28718                     data : [
28719                         ['100%'],
28720                         ['80%'],
28721                         ['50%'],
28722                         ['20%'],
28723                         ['10%']
28724                     ],
28725                     fields : [ 'val'],
28726                     xns : Roo.data
28727                 }
28728             },
28729             {
28730                 xtype : 'TextItem',
28731                 text : "Align: ",
28732                 xns : rooui.Toolbar  //Boostrap?
28733             },
28734             {
28735                 xtype : 'ComboBox',
28736                 allowBlank : false,
28737                 displayField : 'val',
28738                 editable : true,
28739                 listWidth : 100,
28740                 triggerAction : 'all',
28741                 typeAhead : true,
28742                 valueField : 'val',
28743                 width : 70,
28744                 name : 'align',
28745                 listeners : {
28746                     select : function (combo, r, index)
28747                     {
28748                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28749                         var b = block();
28750                         b.align = r.get('val');
28751                         b.updateElement();
28752                         syncValue();
28753                         toolbar.editorcore.onEditorEvent();
28754                     }
28755                 },
28756                 xns : rooui.form,
28757                 store : {
28758                     xtype : 'SimpleStore',
28759                     data : [
28760                         ['left'],
28761                         ['right'],
28762                         ['center']
28763                     ],
28764                     fields : [ 'val'],
28765                     xns : Roo.data
28766                 }
28767             },
28768             
28769             
28770             {
28771                 xtype : 'Button',
28772                 text: 'Hide Caption',
28773                 name : 'caption_display',
28774                 pressed : false,
28775                 enableToggle : true,
28776                 setValue : function(v) {
28777                     // this trigger toggle.
28778                      
28779                     this.setText(v ? "Hide Caption" : "Show Caption");
28780                     this.setPressed(v != 'block');
28781                 },
28782                 listeners : {
28783                     toggle: function (btn, state)
28784                     {
28785                         var b  = block();
28786                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
28787                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
28788                         b.updateElement();
28789                         syncValue();
28790                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28791                         toolbar.editorcore.onEditorEvent();
28792                     }
28793                 },
28794                 xns : rooui.Toolbar
28795             }
28796         ];
28797         
28798     },
28799     /**
28800      * create a DomHelper friendly object - for use with
28801      * Roo.DomHelper.markup / overwrite / etc..
28802      */
28803     toObject : function()
28804     {
28805         var d = document.createElement('div');
28806         d.innerHTML = this.caption;
28807         
28808         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
28809         
28810         var iw = this.align == 'center' ? this.width : '100%';
28811         var img =   {
28812             tag : 'img',
28813             contenteditable : 'false',
28814             src : this.image_src,
28815             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
28816             style: {
28817                 width : iw,
28818                 maxWidth : iw + ' !important', // this is not getting rendered?
28819                 margin : m  
28820                 
28821             }
28822         };
28823         /*
28824         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
28825                     '<a href="{2}">' + 
28826                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
28827                     '</a>' + 
28828                 '</div>',
28829         */
28830                 
28831         if (this.href.length > 0) {
28832             img = {
28833                 tag : 'a',
28834                 href: this.href,
28835                 contenteditable : 'true',
28836                 cn : [
28837                     img
28838                 ]
28839             };
28840         }
28841         
28842         
28843         if (this.video_url.length > 0) {
28844             img = {
28845                 tag : 'div',
28846                 cls : this.cls,
28847                 frameborder : 0,
28848                 allowfullscreen : true,
28849                 width : 420,  // these are for video tricks - that we replace the outer
28850                 height : 315,
28851                 src : this.video_url,
28852                 cn : [
28853                     img
28854                 ]
28855             };
28856         }
28857         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
28858         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
28859         
28860   
28861         var ret =   {
28862             tag: 'figure',
28863             'data-block' : 'Figure',
28864             'data-width' : this.width, 
28865             contenteditable : 'false',
28866             
28867             style : {
28868                 display: 'block',
28869                 float :  this.align ,
28870                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
28871                 width : this.align == 'center' ? '100%' : this.width,
28872                 margin:  '0px',
28873                 padding: this.align == 'center' ? '0' : '0 10px' ,
28874                 textAlign : this.align   // seems to work for email..
28875                 
28876             },
28877            
28878             
28879             align : this.align,
28880             cn : [
28881                 img,
28882               
28883                 {
28884                     tag: 'figcaption',
28885                     'data-display' : this.caption_display,
28886                     style : {
28887                         textAlign : 'left',
28888                         fontSize : '16px',
28889                         lineHeight : '24px',
28890                         display : this.caption_display,
28891                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
28892                         margin: m,
28893                         width: this.align == 'center' ?  this.width : '100%' 
28894                     
28895                          
28896                     },
28897                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
28898                     cn : [
28899                         {
28900                             tag: 'div',
28901                             style  : {
28902                                 marginTop : '16px',
28903                                 textAlign : 'left'
28904                             },
28905                             align: 'left',
28906                             cn : [
28907                                 {
28908                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
28909                                     tag : 'i',
28910                                     contenteditable : true,
28911                                     html : captionhtml
28912                                 }
28913                                 
28914                             ]
28915                         }
28916                         
28917                     ]
28918                     
28919                 }
28920             ]
28921         };
28922         return ret;
28923          
28924     },
28925     
28926     readElement : function(node)
28927     {
28928         // this should not really come from the link...
28929         this.video_url = this.getVal(node, 'div', 'src');
28930         this.cls = this.getVal(node, 'div', 'class');
28931         this.href = this.getVal(node, 'a', 'href');
28932         
28933         
28934         this.image_src = this.getVal(node, 'img', 'src');
28935          
28936         this.align = this.getVal(node, 'figure', 'align');
28937         var figcaption = this.getVal(node, 'figcaption', false);
28938         if (figcaption !== '') {
28939             this.caption = this.getVal(figcaption, 'i', 'html');
28940         }
28941         
28942
28943         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
28944         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
28945         this.width = this.getVal(node, true, 'data-width');
28946         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
28947         
28948     },
28949     removeNode : function()
28950     {
28951         return this.node;
28952     }
28953     
28954   
28955    
28956      
28957     
28958     
28959     
28960     
28961 })
28962
28963  
28964
28965 /**
28966  * @class Roo.htmleditor.BlockTable
28967  * Block that manages a table
28968  * 
28969  * @constructor
28970  * Create a new Filter.
28971  * @param {Object} config Configuration options
28972  */
28973
28974 Roo.htmleditor.BlockTable = function(cfg)
28975 {
28976     if (cfg.node) {
28977         this.readElement(cfg.node);
28978         this.updateElement(cfg.node);
28979     }
28980     Roo.apply(this, cfg);
28981     if (!cfg.node) {
28982         this.rows = [];
28983         for(var r = 0; r < this.no_row; r++) {
28984             this.rows[r] = [];
28985             for(var c = 0; c < this.no_col; c++) {
28986                 this.rows[r][c] = this.emptyCell();
28987             }
28988         }
28989     }
28990     
28991     
28992 }
28993 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
28994  
28995     rows : false,
28996     no_col : 1,
28997     no_row : 1,
28998     
28999     
29000     width: '100%',
29001     
29002     // used by context menu
29003     friendly_name : 'Table',
29004     deleteTitle : 'Delete Table',
29005     // context menu is drawn once..
29006     
29007     contextMenu : function(toolbar)
29008     {
29009         
29010         var block = function() {
29011             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29012         };
29013         
29014         
29015         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29016         
29017         var syncValue = toolbar.editorcore.syncValue;
29018         
29019         var fields = {};
29020         
29021         return [
29022             {
29023                 xtype : 'TextItem',
29024                 text : "Width: ",
29025                 xns : rooui.Toolbar  //Boostrap?
29026             },
29027             {
29028                 xtype : 'ComboBox',
29029                 allowBlank : false,
29030                 displayField : 'val',
29031                 editable : true,
29032                 listWidth : 100,
29033                 triggerAction : 'all',
29034                 typeAhead : true,
29035                 valueField : 'val',
29036                 width : 100,
29037                 name : 'width',
29038                 listeners : {
29039                     select : function (combo, r, index)
29040                     {
29041                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29042                         var b = block();
29043                         b.width = r.get('val');
29044                         b.updateElement();
29045                         syncValue();
29046                         toolbar.editorcore.onEditorEvent();
29047                     }
29048                 },
29049                 xns : rooui.form,
29050                 store : {
29051                     xtype : 'SimpleStore',
29052                     data : [
29053                         ['100%'],
29054                         ['auto']
29055                     ],
29056                     fields : [ 'val'],
29057                     xns : Roo.data
29058                 }
29059             },
29060             // -------- Cols
29061             
29062             {
29063                 xtype : 'TextItem',
29064                 text : "Columns: ",
29065                 xns : rooui.Toolbar  //Boostrap?
29066             },
29067          
29068             {
29069                 xtype : 'Button',
29070                 text: '-',
29071                 listeners : {
29072                     click : function (_self, e)
29073                     {
29074                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29075                         block().removeColumn();
29076                         syncValue();
29077                         toolbar.editorcore.onEditorEvent();
29078                     }
29079                 },
29080                 xns : rooui.Toolbar
29081             },
29082             {
29083                 xtype : 'Button',
29084                 text: '+',
29085                 listeners : {
29086                     click : function (_self, e)
29087                     {
29088                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29089                         block().addColumn();
29090                         syncValue();
29091                         toolbar.editorcore.onEditorEvent();
29092                     }
29093                 },
29094                 xns : rooui.Toolbar
29095             },
29096             // -------- ROWS
29097             {
29098                 xtype : 'TextItem',
29099                 text : "Rows: ",
29100                 xns : rooui.Toolbar  //Boostrap?
29101             },
29102          
29103             {
29104                 xtype : 'Button',
29105                 text: '-',
29106                 listeners : {
29107                     click : function (_self, e)
29108                     {
29109                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29110                         block().removeRow();
29111                         syncValue();
29112                         toolbar.editorcore.onEditorEvent();
29113                     }
29114                 },
29115                 xns : rooui.Toolbar
29116             },
29117             {
29118                 xtype : 'Button',
29119                 text: '+',
29120                 listeners : {
29121                     click : function (_self, e)
29122                     {
29123                         block().addRow();
29124                         syncValue();
29125                         toolbar.editorcore.onEditorEvent();
29126                     }
29127                 },
29128                 xns : rooui.Toolbar
29129             },
29130             // -------- ROWS
29131             {
29132                 xtype : 'Button',
29133                 text: 'Reset Column Widths',
29134                 listeners : {
29135                     
29136                     click : function (_self, e)
29137                     {
29138                         block().resetWidths();
29139                         syncValue();
29140                         toolbar.editorcore.onEditorEvent();
29141                     }
29142                 },
29143                 xns : rooui.Toolbar
29144             } 
29145             
29146             
29147             
29148         ];
29149         
29150     },
29151     
29152     
29153   /**
29154      * create a DomHelper friendly object - for use with
29155      * Roo.DomHelper.markup / overwrite / etc..
29156      * ?? should it be called with option to hide all editing features?
29157      */
29158     toObject : function()
29159     {
29160         
29161         var ret = {
29162             tag : 'table',
29163             contenteditable : 'false', // this stops cell selection from picking the table.
29164             'data-block' : 'Table',
29165             style : {
29166                 width:  this.width,
29167                 border : 'solid 1px #000', // ??? hard coded?
29168                 'border-collapse' : 'collapse' 
29169             },
29170             cn : [
29171                 { tag : 'tbody' , cn : [] }
29172             ]
29173         };
29174         
29175         // do we have a head = not really 
29176         var ncols = 0;
29177         Roo.each(this.rows, function( row ) {
29178             var tr = {
29179                 tag: 'tr',
29180                 style : {
29181                     margin: '6px',
29182                     border : 'solid 1px #000',
29183                     textAlign : 'left' 
29184                 },
29185                 cn : [ ]
29186             };
29187             
29188             ret.cn[0].cn.push(tr);
29189             // does the row have any properties? ?? height?
29190             var nc = 0;
29191             Roo.each(row, function( cell ) {
29192                 
29193                 var td = {
29194                     tag : 'td',
29195                     contenteditable :  'true',
29196                     'data-block' : 'Td',
29197                     html : cell.html,
29198                     style : cell.style
29199                 };
29200                 if (cell.colspan > 1) {
29201                     td.colspan = cell.colspan ;
29202                     nc += cell.colspan;
29203                 } else {
29204                     nc++;
29205                 }
29206                 if (cell.rowspan > 1) {
29207                     td.rowspan = cell.rowspan ;
29208                 }
29209                 
29210                 
29211                 // widths ?
29212                 tr.cn.push(td);
29213                     
29214                 
29215             }, this);
29216             ncols = Math.max(nc, ncols);
29217             
29218             
29219         }, this);
29220         // add the header row..
29221         
29222         ncols++;
29223          
29224         
29225         return ret;
29226          
29227     },
29228     
29229     readElement : function(node)
29230     {
29231         node  = node ? node : this.node ;
29232         this.width = this.getVal(node, true, 'style', 'width') || '100%';
29233         
29234         this.rows = [];
29235         this.no_row = 0;
29236         var trs = Array.from(node.rows);
29237         trs.forEach(function(tr) {
29238             var row =  [];
29239             this.rows.push(row);
29240             
29241             this.no_row++;
29242             var no_column = 0;
29243             Array.from(tr.cells).forEach(function(td) {
29244                 
29245                 var add = {
29246                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29247                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29248                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29249                     html : td.innerHTML
29250                 };
29251                 no_column += add.colspan;
29252                      
29253                 
29254                 row.push(add);
29255                 
29256                 
29257             },this);
29258             this.no_col = Math.max(this.no_col, no_column);
29259             
29260             
29261         },this);
29262         
29263         
29264     },
29265     normalizeRows: function()
29266     {
29267         var ret= [];
29268         var rid = -1;
29269         this.rows.forEach(function(row) {
29270             rid++;
29271             ret[rid] = [];
29272             row = this.normalizeRow(row);
29273             var cid = 0;
29274             row.forEach(function(c) {
29275                 while (typeof(ret[rid][cid]) != 'undefined') {
29276                     cid++;
29277                 }
29278                 if (typeof(ret[rid]) == 'undefined') {
29279                     ret[rid] = [];
29280                 }
29281                 ret[rid][cid] = c;
29282                 c.row = rid;
29283                 c.col = cid;
29284                 if (c.rowspan < 2) {
29285                     return;
29286                 }
29287                 
29288                 for(var i = 1 ;i < c.rowspan; i++) {
29289                     if (typeof(ret[rid+i]) == 'undefined') {
29290                         ret[rid+i] = [];
29291                     }
29292                     ret[rid+i][cid] = c;
29293                 }
29294             });
29295         }, this);
29296         return ret;
29297     
29298     },
29299     
29300     normalizeRow: function(row)
29301     {
29302         var ret= [];
29303         row.forEach(function(c) {
29304             if (c.colspan < 2) {
29305                 ret.push(c);
29306                 return;
29307             }
29308             for(var i =0 ;i < c.colspan; i++) {
29309                 ret.push(c);
29310             }
29311         });
29312         return ret;
29313     
29314     },
29315     
29316     deleteColumn : function(sel)
29317     {
29318         if (!sel || sel.type != 'col') {
29319             return;
29320         }
29321         if (this.no_col < 2) {
29322             return;
29323         }
29324         
29325         this.rows.forEach(function(row) {
29326             var cols = this.normalizeRow(row);
29327             var col = cols[sel.col];
29328             if (col.colspan > 1) {
29329                 col.colspan --;
29330             } else {
29331                 row.remove(col);
29332             }
29333             
29334         }, this);
29335         this.no_col--;
29336         
29337     },
29338     removeColumn : function()
29339     {
29340         this.deleteColumn({
29341             type: 'col',
29342             col : this.no_col-1
29343         });
29344         this.updateElement();
29345     },
29346     
29347      
29348     addColumn : function()
29349     {
29350         
29351         this.rows.forEach(function(row) {
29352             row.push(this.emptyCell());
29353            
29354         }, this);
29355         this.updateElement();
29356     },
29357     
29358     deleteRow : function(sel)
29359     {
29360         if (!sel || sel.type != 'row') {
29361             return;
29362         }
29363         
29364         if (this.no_row < 2) {
29365             return;
29366         }
29367         
29368         var rows = this.normalizeRows();
29369         
29370         
29371         rows[sel.row].forEach(function(col) {
29372             if (col.rowspan > 1) {
29373                 col.rowspan--;
29374             } else {
29375                 col.remove = 1; // flage it as removed.
29376             }
29377             
29378         }, this);
29379         var newrows = [];
29380         this.rows.forEach(function(row) {
29381             newrow = [];
29382             row.forEach(function(c) {
29383                 if (typeof(c.remove) == 'undefined') {
29384                     newrow.push(c);
29385                 }
29386                 
29387             });
29388             if (newrow.length > 0) {
29389                 newrows.push(row);
29390             }
29391         });
29392         this.rows =  newrows;
29393         
29394         
29395         
29396         this.no_row--;
29397         this.updateElement();
29398         
29399     },
29400     removeRow : function()
29401     {
29402         this.deleteRow({
29403             type: 'row',
29404             row : this.no_row-1
29405         });
29406         
29407     },
29408     
29409      
29410     addRow : function()
29411     {
29412         
29413         var row = [];
29414         for (var i = 0; i < this.no_col; i++ ) {
29415             
29416             row.push(this.emptyCell());
29417            
29418         }
29419         this.rows.push(row);
29420         this.updateElement();
29421         
29422     },
29423      
29424     // the default cell object... at present...
29425     emptyCell : function() {
29426         return (new Roo.htmleditor.BlockTd({})).toObject();
29427         
29428      
29429     },
29430     
29431     removeNode : function()
29432     {
29433         return this.node;
29434     },
29435     
29436     
29437     
29438     resetWidths : function()
29439     {
29440         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
29441             var nn = Roo.htmleditor.Block.factory(n);
29442             nn.width = '';
29443             nn.updateElement(n);
29444         });
29445     }
29446     
29447     
29448     
29449     
29450 })
29451
29452 /**
29453  *
29454  * editing a TD?
29455  *
29456  * since selections really work on the table cell, then editing really should work from there
29457  *
29458  * The original plan was to support merging etc... - but that may not be needed yet..
29459  *
29460  * So this simple version will support:
29461  *   add/remove cols
29462  *   adjust the width +/-
29463  *   reset the width...
29464  *   
29465  *
29466  */
29467
29468
29469  
29470
29471 /**
29472  * @class Roo.htmleditor.BlockTable
29473  * Block that manages a table
29474  * 
29475  * @constructor
29476  * Create a new Filter.
29477  * @param {Object} config Configuration options
29478  */
29479
29480 Roo.htmleditor.BlockTd = function(cfg)
29481 {
29482     if (cfg.node) {
29483         this.readElement(cfg.node);
29484         this.updateElement(cfg.node);
29485     }
29486     Roo.apply(this, cfg);
29487      
29488     
29489     
29490 }
29491 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
29492  
29493     node : false,
29494     
29495     width: '',
29496     textAlign : 'left',
29497     valign : 'top',
29498     
29499     colspan : 1,
29500     rowspan : 1,
29501     
29502     
29503     // used by context menu
29504     friendly_name : 'Table Cell',
29505     deleteTitle : false, // use our customer delete
29506     
29507     // context menu is drawn once..
29508     
29509     contextMenu : function(toolbar)
29510     {
29511         
29512         var cell = function() {
29513             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29514         };
29515         
29516         var table = function() {
29517             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
29518         };
29519         
29520         var lr = false;
29521         var saveSel = function()
29522         {
29523             lr = toolbar.editorcore.getSelection().getRangeAt(0);
29524         }
29525         var restoreSel = function()
29526         {
29527             if (lr) {
29528                 (function() {
29529                     toolbar.editorcore.focus();
29530                     var cr = toolbar.editorcore.getSelection();
29531                     cr.removeAllRanges();
29532                     cr.addRange(lr);
29533                     toolbar.editorcore.onEditorEvent();
29534                 }).defer(10, this);
29535                 
29536                 
29537             }
29538         }
29539         
29540         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29541         
29542         var syncValue = toolbar.editorcore.syncValue;
29543         
29544         var fields = {};
29545         
29546         return [
29547             {
29548                 xtype : 'Button',
29549                 text : 'Edit Table',
29550                 listeners : {
29551                     click : function() {
29552                         var t = toolbar.tb.selectedNode.closest('table');
29553                         toolbar.editorcore.selectNode(t);
29554                         toolbar.editorcore.onEditorEvent();                        
29555                     }
29556                 }
29557                 
29558             },
29559               
29560            
29561              
29562             {
29563                 xtype : 'TextItem',
29564                 text : "Column Width: ",
29565                  xns : rooui.Toolbar 
29566                
29567             },
29568             {
29569                 xtype : 'Button',
29570                 text: '-',
29571                 listeners : {
29572                     click : function (_self, e)
29573                     {
29574                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29575                         cell().shrinkColumn();
29576                         syncValue();
29577                          toolbar.editorcore.onEditorEvent();
29578                     }
29579                 },
29580                 xns : rooui.Toolbar
29581             },
29582             {
29583                 xtype : 'Button',
29584                 text: '+',
29585                 listeners : {
29586                     click : function (_self, e)
29587                     {
29588                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29589                         cell().growColumn();
29590                         syncValue();
29591                         toolbar.editorcore.onEditorEvent();
29592                     }
29593                 },
29594                 xns : rooui.Toolbar
29595             },
29596             
29597             {
29598                 xtype : 'TextItem',
29599                 text : "Vertical Align: ",
29600                 xns : rooui.Toolbar  //Boostrap?
29601             },
29602             {
29603                 xtype : 'ComboBox',
29604                 allowBlank : false,
29605                 displayField : 'val',
29606                 editable : true,
29607                 listWidth : 100,
29608                 triggerAction : 'all',
29609                 typeAhead : true,
29610                 valueField : 'val',
29611                 width : 100,
29612                 name : 'valign',
29613                 listeners : {
29614                     select : function (combo, r, index)
29615                     {
29616                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29617                         var b = cell();
29618                         b.valign = r.get('val');
29619                         b.updateElement();
29620                         syncValue();
29621                         toolbar.editorcore.onEditorEvent();
29622                     }
29623                 },
29624                 xns : rooui.form,
29625                 store : {
29626                     xtype : 'SimpleStore',
29627                     data : [
29628                         ['top'],
29629                         ['middle'],
29630                         ['bottom'] // there are afew more... 
29631                     ],
29632                     fields : [ 'val'],
29633                     xns : Roo.data
29634                 }
29635             },
29636             
29637             {
29638                 xtype : 'TextItem',
29639                 text : "Merge Cells: ",
29640                  xns : rooui.Toolbar 
29641                
29642             },
29643             
29644             
29645             {
29646                 xtype : 'Button',
29647                 text: 'Right',
29648                 listeners : {
29649                     click : function (_self, e)
29650                     {
29651                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29652                         cell().mergeRight();
29653                         //block().growColumn();
29654                         syncValue();
29655                         toolbar.editorcore.onEditorEvent();
29656                     }
29657                 },
29658                 xns : rooui.Toolbar
29659             },
29660              
29661             {
29662                 xtype : 'Button',
29663                 text: 'Below',
29664                 listeners : {
29665                     click : function (_self, e)
29666                     {
29667                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29668                         cell().mergeBelow();
29669                         //block().growColumn();
29670                         syncValue();
29671                         toolbar.editorcore.onEditorEvent();
29672                     }
29673                 },
29674                 xns : rooui.Toolbar
29675             },
29676             {
29677                 xtype : 'TextItem',
29678                 text : "| ",
29679                  xns : rooui.Toolbar 
29680                
29681             },
29682             
29683             {
29684                 xtype : 'Button',
29685                 text: 'Split',
29686                 listeners : {
29687                     click : function (_self, e)
29688                     {
29689                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29690                         cell().split();
29691                         syncValue();
29692                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29693                         toolbar.editorcore.onEditorEvent();
29694                                              
29695                     }
29696                 },
29697                 xns : rooui.Toolbar
29698             },
29699             {
29700                 xtype : 'Fill',
29701                 xns : rooui.Toolbar 
29702                
29703             },
29704         
29705           
29706             {
29707                 xtype : 'Button',
29708                 text: 'Delete',
29709                  
29710                 xns : rooui.Toolbar,
29711                 menu : {
29712                     xtype : 'Menu',
29713                     xns : rooui.menu,
29714                     items : [
29715                         {
29716                             xtype : 'Item',
29717                             html: 'Column',
29718                             listeners : {
29719                                 click : function (_self, e)
29720                                 {
29721                                     var t = table();
29722                                     
29723                                     cell().deleteColumn();
29724                                     syncValue();
29725                                     toolbar.editorcore.selectNode(t.node);
29726                                     toolbar.editorcore.onEditorEvent();   
29727                                 }
29728                             },
29729                             xns : rooui.menu
29730                         },
29731                         {
29732                             xtype : 'Item',
29733                             html: 'Row',
29734                             listeners : {
29735                                 click : function (_self, e)
29736                                 {
29737                                     var t = table();
29738                                     cell().deleteRow();
29739                                     syncValue();
29740                                     
29741                                     toolbar.editorcore.selectNode(t.node);
29742                                     toolbar.editorcore.onEditorEvent();   
29743                                                          
29744                                 }
29745                             },
29746                             xns : rooui.menu
29747                         },
29748                        {
29749                             xtype : 'Separator',
29750                             xns : rooui.menu
29751                         },
29752                         {
29753                             xtype : 'Item',
29754                             html: 'Table',
29755                             listeners : {
29756                                 click : function (_self, e)
29757                                 {
29758                                     var t = table();
29759                                     var nn = t.node.nextSibling || t.node.previousSibling;
29760                                     t.node.parentNode.removeChild(t.node);
29761                                     if (nn) { 
29762                                         toolbar.editorcore.selectNode(nn, true);
29763                                     }
29764                                     toolbar.editorcore.onEditorEvent();   
29765                                                          
29766                                 }
29767                             },
29768                             xns : rooui.menu
29769                         }
29770                     ]
29771                 }
29772             }
29773             
29774             // align... << fixme
29775             
29776         ];
29777         
29778     },
29779     
29780     
29781   /**
29782      * create a DomHelper friendly object - for use with
29783      * Roo.DomHelper.markup / overwrite / etc..
29784      * ?? should it be called with option to hide all editing features?
29785      */
29786  /**
29787      * create a DomHelper friendly object - for use with
29788      * Roo.DomHelper.markup / overwrite / etc..
29789      * ?? should it be called with option to hide all editing features?
29790      */
29791     toObject : function()
29792     {
29793         var ret = {
29794             tag : 'td',
29795             contenteditable : 'true', // this stops cell selection from picking the table.
29796             'data-block' : 'Td',
29797             valign : this.valign,
29798             style : {  
29799                 'text-align' :  this.textAlign,
29800                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
29801                 'border-collapse' : 'collapse',
29802                 padding : '6px', // 8 for desktop / 4 for mobile
29803                 'vertical-align': this.valign
29804             },
29805             html : this.html
29806         };
29807         if (this.width != '') {
29808             ret.width = this.width;
29809             ret.style.width = this.width;
29810         }
29811         
29812         
29813         if (this.colspan > 1) {
29814             ret.colspan = this.colspan ;
29815         } 
29816         if (this.rowspan > 1) {
29817             ret.rowspan = this.rowspan ;
29818         }
29819         
29820            
29821         
29822         return ret;
29823          
29824     },
29825     
29826     readElement : function(node)
29827     {
29828         node  = node ? node : this.node ;
29829         this.width = node.style.width;
29830         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
29831         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
29832         this.html = node.innerHTML;
29833         if (node.style.textAlign != '') {
29834             this.textAlign = node.style.textAlign;
29835         }
29836         
29837         
29838     },
29839      
29840     // the default cell object... at present...
29841     emptyCell : function() {
29842         return {
29843             colspan :  1,
29844             rowspan :  1,
29845             textAlign : 'left',
29846             html : "&nbsp;" // is this going to be editable now?
29847         };
29848      
29849     },
29850     
29851     removeNode : function()
29852     {
29853         return this.node.closest('table');
29854          
29855     },
29856     
29857     cellData : false,
29858     
29859     colWidths : false,
29860     
29861     toTableArray  : function()
29862     {
29863         var ret = [];
29864         var tab = this.node.closest('tr').closest('table');
29865         Array.from(tab.rows).forEach(function(r, ri){
29866             ret[ri] = [];
29867         });
29868         var rn = 0;
29869         this.colWidths = [];
29870         var all_auto = true;
29871         Array.from(tab.rows).forEach(function(r, ri){
29872             
29873             var cn = 0;
29874             Array.from(r.cells).forEach(function(ce, ci){
29875                 var c =  {
29876                     cell : ce,
29877                     row : rn,
29878                     col: cn,
29879                     colspan : ce.colSpan,
29880                     rowspan : ce.rowSpan
29881                 };
29882                 if (ce.isEqualNode(this.node)) {
29883                     this.cellData = c;
29884                 }
29885                 // if we have been filled up by a row?
29886                 if (typeof(ret[rn][cn]) != 'undefined') {
29887                     while(typeof(ret[rn][cn]) != 'undefined') {
29888                         cn++;
29889                     }
29890                     c.col = cn;
29891                 }
29892                 
29893                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
29894                     this.colWidths[cn] =   ce.style.width;
29895                     if (this.colWidths[cn] != '') {
29896                         all_auto = false;
29897                     }
29898                 }
29899                 
29900                 
29901                 if (c.colspan < 2 && c.rowspan < 2 ) {
29902                     ret[rn][cn] = c;
29903                     cn++;
29904                     return;
29905                 }
29906                 for(var j = 0; j < c.rowspan; j++) {
29907                     if (typeof(ret[rn+j]) == 'undefined') {
29908                         continue; // we have a problem..
29909                     }
29910                     ret[rn+j][cn] = c;
29911                     for(var i = 0; i < c.colspan; i++) {
29912                         ret[rn+j][cn+i] = c;
29913                     }
29914                 }
29915                 
29916                 cn += c.colspan;
29917             }, this);
29918             rn++;
29919         }, this);
29920         
29921         // initalize widths.?
29922         // either all widths or no widths..
29923         if (all_auto) {
29924             this.colWidths[0] = false; // no widths flag.
29925         }
29926         
29927         
29928         return ret;
29929         
29930     },
29931     
29932     
29933     
29934     
29935     mergeRight: function()
29936     {
29937          
29938         // get the contents of the next cell along..
29939         var tr = this.node.closest('tr');
29940         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
29941         if (i >= tr.childNodes.length - 1) {
29942             return; // no cells on right to merge with.
29943         }
29944         var table = this.toTableArray();
29945         
29946         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
29947             return; // nothing right?
29948         }
29949         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
29950         // right cell - must be same rowspan and on the same row.
29951         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
29952             return; // right hand side is not same rowspan.
29953         }
29954         
29955         
29956         
29957         this.node.innerHTML += ' ' + rc.cell.innerHTML;
29958         tr.removeChild(rc.cell);
29959         this.colspan += rc.colspan;
29960         this.node.setAttribute('colspan', this.colspan);
29961
29962         var table = this.toTableArray();
29963         this.normalizeWidths(table);
29964         this.updateWidths(table);
29965     },
29966     
29967     
29968     mergeBelow : function()
29969     {
29970         var table = this.toTableArray();
29971         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
29972             return; // no row below
29973         }
29974         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
29975             return; // nothing right?
29976         }
29977         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
29978         
29979         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
29980             return; // right hand side is not same rowspan.
29981         }
29982         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
29983         rc.cell.parentNode.removeChild(rc.cell);
29984         this.rowspan += rc.rowspan;
29985         this.node.setAttribute('rowspan', this.rowspan);
29986     },
29987     
29988     split: function()
29989     {
29990         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
29991             return;
29992         }
29993         var table = this.toTableArray();
29994         var cd = this.cellData;
29995         this.rowspan = 1;
29996         this.colspan = 1;
29997         
29998         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
29999              
30000             
30001             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30002                 if (r == cd.row && c == cd.col) {
30003                     this.node.removeAttribute('rowspan');
30004                     this.node.removeAttribute('colspan');
30005                 }
30006                  
30007                 var ntd = this.node.cloneNode(); // which col/row should be 0..
30008                 ntd.removeAttribute('id'); 
30009                 ntd.style.width  = this.colWidths[c];
30010                 ntd.innerHTML = '';
30011                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
30012             }
30013             
30014         }
30015         this.redrawAllCells(table);
30016         
30017     },
30018     
30019     
30020     
30021     redrawAllCells: function(table)
30022     {
30023         
30024          
30025         var tab = this.node.closest('tr').closest('table');
30026         var ctr = tab.rows[0].parentNode;
30027         Array.from(tab.rows).forEach(function(r, ri){
30028             
30029             Array.from(r.cells).forEach(function(ce, ci){
30030                 ce.parentNode.removeChild(ce);
30031             });
30032             r.parentNode.removeChild(r);
30033         });
30034         for(var r = 0 ; r < table.length; r++) {
30035             var re = tab.rows[r];
30036             
30037             var re = tab.ownerDocument.createElement('tr');
30038             ctr.appendChild(re);
30039             for(var c = 0 ; c < table[r].length; c++) {
30040                 if (table[r][c].cell === false) {
30041                     continue;
30042                 }
30043                 
30044                 re.appendChild(table[r][c].cell);
30045                  
30046                 table[r][c].cell = false;
30047             }
30048         }
30049         
30050     },
30051     updateWidths : function(table)
30052     {
30053         for(var r = 0 ; r < table.length; r++) {
30054            
30055             for(var c = 0 ; c < table[r].length; c++) {
30056                 if (table[r][c].cell === false) {
30057                     continue;
30058                 }
30059                 
30060                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30061                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30062                     el.width = Math.floor(this.colWidths[c])  +'%';
30063                     el.updateElement(el.node);
30064                 }
30065                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30066                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30067                     var width = 0;
30068                     for(var i = 0; i < table[r][c].colspan; i ++) {
30069                         width += Math.floor(this.colWidths[c + i]);
30070                     }
30071                     el.width = width  +'%';
30072                     el.updateElement(el.node);
30073                 }
30074                 table[r][c].cell = false; // done
30075             }
30076         }
30077     },
30078     normalizeWidths : function(table)
30079     {
30080         if (this.colWidths[0] === false) {
30081             var nw = 100.0 / this.colWidths.length;
30082             this.colWidths.forEach(function(w,i) {
30083                 this.colWidths[i] = nw;
30084             },this);
30085             return;
30086         }
30087     
30088         var t = 0, missing = [];
30089         
30090         this.colWidths.forEach(function(w,i) {
30091             //if you mix % and
30092             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30093             var add =  this.colWidths[i];
30094             if (add > 0) {
30095                 t+=add;
30096                 return;
30097             }
30098             missing.push(i);
30099             
30100             
30101         },this);
30102         var nc = this.colWidths.length;
30103         if (missing.length) {
30104             var mult = (nc - missing.length) / (1.0 * nc);
30105             var t = mult * t;
30106             var ew = (100 -t) / (1.0 * missing.length);
30107             this.colWidths.forEach(function(w,i) {
30108                 if (w > 0) {
30109                     this.colWidths[i] = w * mult;
30110                     return;
30111                 }
30112                 
30113                 this.colWidths[i] = ew;
30114             }, this);
30115             // have to make up numbers..
30116              
30117         }
30118         // now we should have all the widths..
30119         
30120     
30121     },
30122     
30123     shrinkColumn : function()
30124     {
30125         var table = this.toTableArray();
30126         this.normalizeWidths(table);
30127         var col = this.cellData.col;
30128         var nw = this.colWidths[col] * 0.8;
30129         if (nw < 5) {
30130             return;
30131         }
30132         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30133         this.colWidths.forEach(function(w,i) {
30134             if (i == col) {
30135                  this.colWidths[i] = nw;
30136                 return;
30137             }
30138             this.colWidths[i] += otherAdd
30139         }, this);
30140         this.updateWidths(table);
30141          
30142     },
30143     growColumn : function()
30144     {
30145         var table = this.toTableArray();
30146         this.normalizeWidths(table);
30147         var col = this.cellData.col;
30148         var nw = this.colWidths[col] * 1.2;
30149         if (nw > 90) {
30150             return;
30151         }
30152         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30153         this.colWidths.forEach(function(w,i) {
30154             if (i == col) {
30155                 this.colWidths[i] = nw;
30156                 return;
30157             }
30158             this.colWidths[i] -= otherSub
30159         }, this);
30160         this.updateWidths(table);
30161          
30162     },
30163     deleteRow : function()
30164     {
30165         // delete this rows 'tr'
30166         // if any of the cells in this row have a rowspan > 1 && row!= this row..
30167         // then reduce the rowspan.
30168         var table = this.toTableArray();
30169         // this.cellData.row;
30170         for (var i =0;i< table[this.cellData.row].length ; i++) {
30171             var c = table[this.cellData.row][i];
30172             if (c.row != this.cellData.row) {
30173                 
30174                 c.rowspan--;
30175                 c.cell.setAttribute('rowspan', c.rowspan);
30176                 continue;
30177             }
30178             if (c.rowspan > 1) {
30179                 c.rowspan--;
30180                 c.cell.setAttribute('rowspan', c.rowspan);
30181             }
30182         }
30183         table.splice(this.cellData.row,1);
30184         this.redrawAllCells(table);
30185         
30186     },
30187     deleteColumn : function()
30188     {
30189         var table = this.toTableArray();
30190         
30191         for (var i =0;i< table.length ; i++) {
30192             var c = table[i][this.cellData.col];
30193             if (c.col != this.cellData.col) {
30194                 table[i][this.cellData.col].colspan--;
30195             } else if (c.colspan > 1) {
30196                 c.colspan--;
30197                 c.cell.setAttribute('colspan', c.colspan);
30198             }
30199             table[i].splice(this.cellData.col,1);
30200         }
30201         
30202         this.redrawAllCells(table);
30203     }
30204     
30205     
30206     
30207     
30208 })
30209
30210 //<script type="text/javascript">
30211
30212 /*
30213  * Based  Ext JS Library 1.1.1
30214  * Copyright(c) 2006-2007, Ext JS, LLC.
30215  * LGPL
30216  *
30217  */
30218  
30219 /**
30220  * @class Roo.HtmlEditorCore
30221  * @extends Roo.Component
30222  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30223  *
30224  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30225  */
30226
30227 Roo.HtmlEditorCore = function(config){
30228     
30229     
30230     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30231     
30232     
30233     this.addEvents({
30234         /**
30235          * @event initialize
30236          * Fires when the editor is fully initialized (including the iframe)
30237          * @param {Roo.HtmlEditorCore} this
30238          */
30239         initialize: true,
30240         /**
30241          * @event activate
30242          * Fires when the editor is first receives the focus. Any insertion must wait
30243          * until after this event.
30244          * @param {Roo.HtmlEditorCore} this
30245          */
30246         activate: true,
30247          /**
30248          * @event beforesync
30249          * Fires before the textarea is updated with content from the editor iframe. Return false
30250          * to cancel the sync.
30251          * @param {Roo.HtmlEditorCore} this
30252          * @param {String} html
30253          */
30254         beforesync: true,
30255          /**
30256          * @event beforepush
30257          * Fires before the iframe editor is updated with content from the textarea. Return false
30258          * to cancel the push.
30259          * @param {Roo.HtmlEditorCore} this
30260          * @param {String} html
30261          */
30262         beforepush: true,
30263          /**
30264          * @event sync
30265          * Fires when the textarea is updated with content from the editor iframe.
30266          * @param {Roo.HtmlEditorCore} this
30267          * @param {String} html
30268          */
30269         sync: true,
30270          /**
30271          * @event push
30272          * Fires when the iframe editor is updated with content from the textarea.
30273          * @param {Roo.HtmlEditorCore} this
30274          * @param {String} html
30275          */
30276         push: true,
30277         
30278         /**
30279          * @event editorevent
30280          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30281          * @param {Roo.HtmlEditorCore} this
30282          */
30283         editorevent: true 
30284          
30285         
30286     });
30287     
30288     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30289     
30290     // defaults : white / black...
30291     this.applyBlacklists();
30292     
30293     
30294     
30295 };
30296
30297
30298 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
30299
30300
30301      /**
30302      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
30303      */
30304     
30305     owner : false,
30306     
30307      /**
30308      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
30309      *                        Roo.resizable.
30310      */
30311     resizable : false,
30312      /**
30313      * @cfg {Number} height (in pixels)
30314      */   
30315     height: 300,
30316    /**
30317      * @cfg {Number} width (in pixels)
30318      */   
30319     width: 500,
30320      /**
30321      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30322      *         if you are doing an email editor, this probably needs disabling, it's designed
30323      */
30324     autoClean: true,
30325     
30326     /**
30327      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30328      */
30329     enableBlocks : true,
30330     /**
30331      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30332      * 
30333      */
30334     stylesheets: false,
30335      /**
30336      * @cfg {String} language default en - language of text (usefull for rtl languages)
30337      * 
30338      */
30339     language: 'en',
30340     
30341     /**
30342      * @cfg {boolean} allowComments - default false - allow comments in HTML source
30343      *          - by default they are stripped - if you are editing email you may need this.
30344      */
30345     allowComments: false,
30346     // id of frame..
30347     frameId: false,
30348     
30349     // private properties
30350     validationEvent : false,
30351     deferHeight: true,
30352     initialized : false,
30353     activated : false,
30354     sourceEditMode : false,
30355     onFocus : Roo.emptyFn,
30356     iframePad:3,
30357     hideMode:'offsets',
30358     
30359     clearUp: true,
30360     
30361     // blacklist + whitelisted elements..
30362     black: false,
30363     white: false,
30364      
30365     bodyCls : '',
30366
30367     
30368     undoManager : false,
30369     /**
30370      * Protected method that will not generally be called directly. It
30371      * is called when the editor initializes the iframe with HTML contents. Override this method if you
30372      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30373      */
30374     getDocMarkup : function(){
30375         // body styles..
30376         var st = '';
30377         
30378         // inherit styels from page...?? 
30379         if (this.stylesheets === false) {
30380             
30381             Roo.get(document.head).select('style').each(function(node) {
30382                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30383             });
30384             
30385             Roo.get(document.head).select('link').each(function(node) { 
30386                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30387             });
30388             
30389         } else if (!this.stylesheets.length) {
30390                 // simple..
30391                 st = '<style type="text/css">' +
30392                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30393                    '</style>';
30394         } else {
30395             for (var i in this.stylesheets) {
30396                 if (typeof(this.stylesheets[i]) != 'string') {
30397                     continue;
30398                 }
30399                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30400             }
30401             
30402         }
30403         
30404         st +=  '<style type="text/css">' +
30405             'IMG { cursor: pointer } ' +
30406         '</style>';
30407         
30408         st += '<meta name="google" content="notranslate">';
30409         
30410         var cls = 'notranslate roo-htmleditor-body';
30411         
30412         if(this.bodyCls.length){
30413             cls += ' ' + this.bodyCls;
30414         }
30415         
30416         return '<html  class="notranslate" translate="no"><head>' + st  +
30417             //<style type="text/css">' +
30418             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30419             //'</style>' +
30420             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
30421     },
30422
30423     // private
30424     onRender : function(ct, position)
30425     {
30426         var _t = this;
30427         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
30428         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
30429         
30430         
30431         this.el.dom.style.border = '0 none';
30432         this.el.dom.setAttribute('tabIndex', -1);
30433         this.el.addClass('x-hidden hide');
30434         
30435         
30436         
30437         if(Roo.isIE){ // fix IE 1px bogus margin
30438             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
30439         }
30440        
30441         
30442         this.frameId = Roo.id();
30443         
30444          
30445         
30446         var iframe = this.owner.wrap.createChild({
30447             tag: 'iframe',
30448             cls: 'form-control', // bootstrap..
30449             id: this.frameId,
30450             name: this.frameId,
30451             frameBorder : 'no',
30452             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
30453         }, this.el
30454         );
30455         
30456         
30457         this.iframe = iframe.dom;
30458
30459         this.assignDocWin();
30460         
30461         this.doc.designMode = 'on';
30462        
30463         this.doc.open();
30464         this.doc.write(this.getDocMarkup());
30465         this.doc.close();
30466
30467         
30468         var task = { // must defer to wait for browser to be ready
30469             run : function(){
30470                 //console.log("run task?" + this.doc.readyState);
30471                 this.assignDocWin();
30472                 if(this.doc.body || this.doc.readyState == 'complete'){
30473                     try {
30474                         this.doc.designMode="on";
30475                         
30476                     } catch (e) {
30477                         return;
30478                     }
30479                     Roo.TaskMgr.stop(task);
30480                     this.initEditor.defer(10, this);
30481                 }
30482             },
30483             interval : 10,
30484             duration: 10000,
30485             scope: this
30486         };
30487         Roo.TaskMgr.start(task);
30488
30489     },
30490
30491     // private
30492     onResize : function(w, h)
30493     {
30494          Roo.log('resize: ' +w + ',' + h );
30495         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
30496         if(!this.iframe){
30497             return;
30498         }
30499         if(typeof w == 'number'){
30500             
30501             this.iframe.style.width = w + 'px';
30502         }
30503         if(typeof h == 'number'){
30504             
30505             this.iframe.style.height = h + 'px';
30506             if(this.doc){
30507                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
30508             }
30509         }
30510         
30511     },
30512
30513     /**
30514      * Toggles the editor between standard and source edit mode.
30515      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
30516      */
30517     toggleSourceEdit : function(sourceEditMode){
30518         
30519         this.sourceEditMode = sourceEditMode === true;
30520         
30521         if(this.sourceEditMode){
30522  
30523             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
30524             
30525         }else{
30526             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
30527             //this.iframe.className = '';
30528             this.deferFocus();
30529         }
30530         //this.setSize(this.owner.wrap.getSize());
30531         //this.fireEvent('editmodechange', this, this.sourceEditMode);
30532     },
30533
30534     
30535   
30536
30537     /**
30538      * Protected method that will not generally be called directly. If you need/want
30539      * custom HTML cleanup, this is the method you should override.
30540      * @param {String} html The HTML to be cleaned
30541      * return {String} The cleaned HTML
30542      */
30543     cleanHtml : function(html)
30544     {
30545         html = String(html);
30546         if(html.length > 5){
30547             if(Roo.isSafari){ // strip safari nonsense
30548                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
30549             }
30550         }
30551         if(html == '&nbsp;'){
30552             html = '';
30553         }
30554         return html;
30555     },
30556
30557     /**
30558      * HTML Editor -> Textarea
30559      * Protected method that will not generally be called directly. Syncs the contents
30560      * of the editor iframe with the textarea.
30561      */
30562     syncValue : function()
30563     {
30564         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
30565         if(this.initialized){
30566             
30567             if (this.undoManager) {
30568                 this.undoManager.addEvent();
30569             }
30570
30571             
30572             var bd = (this.doc.body || this.doc.documentElement);
30573            
30574             
30575             var sel = this.win.getSelection();
30576             
30577             var div = document.createElement('div');
30578             div.innerHTML = bd.innerHTML;
30579             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
30580             if (gtx.length > 0) {
30581                 var rm = gtx.item(0).parentNode;
30582                 rm.parentNode.removeChild(rm);
30583             }
30584             
30585            
30586             if (this.enableBlocks) {
30587                 new Roo.htmleditor.FilterBlock({ node : div });
30588             }
30589             
30590             var html = div.innerHTML;
30591             
30592             //?? tidy?
30593             if (this.autoClean) {
30594                 
30595                 new Roo.htmleditor.FilterAttributes({
30596                     node : div,
30597                     attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
30598                     attrib_clean : ['href', 'src' ] 
30599                 });
30600                 
30601                 var tidy = new Roo.htmleditor.TidySerializer({
30602                     inner:  true
30603                 });
30604                 html  = tidy.serialize(div);
30605                 
30606             }
30607             
30608             
30609             if(Roo.isSafari){
30610                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
30611                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
30612                 if(m && m[1]){
30613                     html = '<div style="'+m[0]+'">' + html + '</div>';
30614                 }
30615             }
30616             html = this.cleanHtml(html);
30617             // fix up the special chars.. normaly like back quotes in word...
30618             // however we do not want to do this with chinese..
30619             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
30620                 
30621                 var cc = match.charCodeAt();
30622
30623                 // Get the character value, handling surrogate pairs
30624                 if (match.length == 2) {
30625                     // It's a surrogate pair, calculate the Unicode code point
30626                     var high = match.charCodeAt(0) - 0xD800;
30627                     var low  = match.charCodeAt(1) - 0xDC00;
30628                     cc = (high * 0x400) + low + 0x10000;
30629                 }  else if (
30630                     (cc >= 0x4E00 && cc < 0xA000 ) ||
30631                     (cc >= 0x3400 && cc < 0x4E00 ) ||
30632                     (cc >= 0xf900 && cc < 0xfb00 )
30633                 ) {
30634                         return match;
30635                 }  
30636          
30637                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
30638                 return "&#" + cc + ";";
30639                 
30640                 
30641             });
30642             
30643             
30644              
30645             if(this.owner.fireEvent('beforesync', this, html) !== false){
30646                 this.el.dom.value = html;
30647                 this.owner.fireEvent('sync', this, html);
30648             }
30649         }
30650     },
30651
30652     /**
30653      * TEXTAREA -> EDITABLE
30654      * Protected method that will not generally be called directly. Pushes the value of the textarea
30655      * into the iframe editor.
30656      */
30657     pushValue : function()
30658     {
30659         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
30660         if(this.initialized){
30661             var v = this.el.dom.value.trim();
30662             
30663             
30664             if(this.owner.fireEvent('beforepush', this, v) !== false){
30665                 var d = (this.doc.body || this.doc.documentElement);
30666                 d.innerHTML = v;
30667                  
30668                 this.el.dom.value = d.innerHTML;
30669                 this.owner.fireEvent('push', this, v);
30670             }
30671             if (this.autoClean) {
30672                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
30673                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
30674             }
30675             if (this.enableBlocks) {
30676                 Roo.htmleditor.Block.initAll(this.doc.body);
30677             }
30678             
30679             this.updateLanguage();
30680             
30681             var lc = this.doc.body.lastChild;
30682             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
30683                 // add an extra line at the end.
30684                 this.doc.body.appendChild(this.doc.createElement('br'));
30685             }
30686             
30687             
30688         }
30689     },
30690
30691     // private
30692     deferFocus : function(){
30693         this.focus.defer(10, this);
30694     },
30695
30696     // doc'ed in Field
30697     focus : function(){
30698         if(this.win && !this.sourceEditMode){
30699             this.win.focus();
30700         }else{
30701             this.el.focus();
30702         }
30703     },
30704     
30705     assignDocWin: function()
30706     {
30707         var iframe = this.iframe;
30708         
30709          if(Roo.isIE){
30710             this.doc = iframe.contentWindow.document;
30711             this.win = iframe.contentWindow;
30712         } else {
30713 //            if (!Roo.get(this.frameId)) {
30714 //                return;
30715 //            }
30716 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
30717 //            this.win = Roo.get(this.frameId).dom.contentWindow;
30718             
30719             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
30720                 return;
30721             }
30722             
30723             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
30724             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
30725         }
30726     },
30727     
30728     // private
30729     initEditor : function(){
30730         //console.log("INIT EDITOR");
30731         this.assignDocWin();
30732         
30733         
30734         
30735         this.doc.designMode="on";
30736         this.doc.open();
30737         this.doc.write(this.getDocMarkup());
30738         this.doc.close();
30739         
30740         var dbody = (this.doc.body || this.doc.documentElement);
30741         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
30742         // this copies styles from the containing element into thsi one..
30743         // not sure why we need all of this..
30744         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
30745         
30746         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
30747         //ss['background-attachment'] = 'fixed'; // w3c
30748         dbody.bgProperties = 'fixed'; // ie
30749         dbody.setAttribute("translate", "no");
30750         
30751         //Roo.DomHelper.applyStyles(dbody, ss);
30752         Roo.EventManager.on(this.doc, {
30753              
30754             'mouseup': this.onEditorEvent,
30755             'dblclick': this.onEditorEvent,
30756             'click': this.onEditorEvent,
30757             'keyup': this.onEditorEvent,
30758             
30759             buffer:100,
30760             scope: this
30761         });
30762         Roo.EventManager.on(this.doc, {
30763             'paste': this.onPasteEvent,
30764             scope : this
30765         });
30766         if(Roo.isGecko){
30767             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
30768         }
30769         //??? needed???
30770         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
30771             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
30772         }
30773         this.initialized = true;
30774
30775         
30776         // initialize special key events - enter
30777         new Roo.htmleditor.KeyEnter({core : this});
30778         
30779          
30780         
30781         this.owner.fireEvent('initialize', this);
30782         this.pushValue();
30783     },
30784     // this is to prevent a href clicks resulting in a redirect?
30785    
30786     onPasteEvent : function(e,v)
30787     {
30788         // I think we better assume paste is going to be a dirty load of rubish from word..
30789         
30790         // even pasting into a 'email version' of this widget will have to clean up that mess.
30791         var cd = (e.browserEvent.clipboardData || window.clipboardData);
30792         
30793         // check what type of paste - if it's an image, then handle it differently.
30794         if (cd.files && cd.files.length > 0) {
30795             // pasting images?
30796             var urlAPI = (window.createObjectURL && window) || 
30797                 (window.URL && URL.revokeObjectURL && URL) || 
30798                 (window.webkitURL && webkitURL);
30799     
30800             var url = urlAPI.createObjectURL( cd.files[0]);
30801             this.insertAtCursor('<img src=" + url + ">');
30802             return false;
30803         }
30804         if (cd.types.indexOf('text/html') < 0 ) {
30805             return false;
30806         }
30807         var images = [];
30808         var html = cd.getData('text/html'); // clipboard event
30809         if (cd.types.indexOf('text/rtf') > -1) {
30810             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
30811             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
30812         }
30813         //Roo.log(images);
30814         //Roo.log(imgs);
30815         // fixme..
30816         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
30817                        .map(function(g) { return g.toDataURL(); })
30818                        .filter(function(g) { return g != 'about:blank'; });
30819         
30820         //Roo.log(html);
30821         html = this.cleanWordChars(html);
30822         
30823         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
30824         
30825         
30826         var sn = this.getParentElement();
30827         // check if d contains a table, and prevent nesting??
30828         //Roo.log(d.getElementsByTagName('table'));
30829         //Roo.log(sn);
30830         //Roo.log(sn.closest('table'));
30831         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
30832             e.preventDefault();
30833             this.insertAtCursor("You can not nest tables");
30834             //Roo.log("prevent?"); // fixme - 
30835             return false;
30836         }
30837         
30838         
30839         
30840         if (images.length > 0) {
30841             // replace all v:imagedata - with img.
30842             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
30843             Roo.each(ar, function(node) {
30844                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
30845                 node.parentNode.removeChild(node);
30846             });
30847             
30848             
30849             Roo.each(d.getElementsByTagName('img'), function(img, i) {
30850                 img.setAttribute('src', images[i]);
30851             });
30852         }
30853         if (this.autoClean) {
30854             new Roo.htmleditor.FilterWord({ node : d });
30855             
30856             new Roo.htmleditor.FilterStyleToTag({ node : d });
30857             new Roo.htmleditor.FilterAttributes({
30858                 node : d,
30859                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
30860                 attrib_clean : ['href', 'src' ] 
30861             });
30862             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
30863             // should be fonts..
30864             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
30865             new Roo.htmleditor.FilterParagraph({ node : d });
30866             new Roo.htmleditor.FilterSpan({ node : d });
30867             new Roo.htmleditor.FilterLongBr({ node : d });
30868             new Roo.htmleditor.FilterComment({ node : d });
30869             
30870             
30871         }
30872         if (this.enableBlocks) {
30873                 
30874             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
30875                 if (img.closest('figure')) { // assume!! that it's aready
30876                     return;
30877                 }
30878                 var fig  = new Roo.htmleditor.BlockFigure({
30879                     image_src  : img.src
30880                 });
30881                 fig.updateElement(img); // replace it..
30882                 
30883             });
30884         }
30885         
30886         
30887         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
30888         if (this.enableBlocks) {
30889             Roo.htmleditor.Block.initAll(this.doc.body);
30890         }
30891          
30892         
30893         e.preventDefault();
30894         return false;
30895         // default behaveiour should be our local cleanup paste? (optional?)
30896         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
30897         //this.owner.fireEvent('paste', e, v);
30898     },
30899     // private
30900     onDestroy : function(){
30901         
30902         
30903         
30904         if(this.rendered){
30905             
30906             //for (var i =0; i < this.toolbars.length;i++) {
30907             //    // fixme - ask toolbars for heights?
30908             //    this.toolbars[i].onDestroy();
30909            // }
30910             
30911             //this.wrap.dom.innerHTML = '';
30912             //this.wrap.remove();
30913         }
30914     },
30915
30916     // private
30917     onFirstFocus : function(){
30918         
30919         this.assignDocWin();
30920         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
30921         
30922         this.activated = true;
30923          
30924     
30925         if(Roo.isGecko){ // prevent silly gecko errors
30926             this.win.focus();
30927             var s = this.win.getSelection();
30928             if(!s.focusNode || s.focusNode.nodeType != 3){
30929                 var r = s.getRangeAt(0);
30930                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
30931                 r.collapse(true);
30932                 this.deferFocus();
30933             }
30934             try{
30935                 this.execCmd('useCSS', true);
30936                 this.execCmd('styleWithCSS', false);
30937             }catch(e){}
30938         }
30939         this.owner.fireEvent('activate', this);
30940     },
30941
30942     // private
30943     adjustFont: function(btn){
30944         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
30945         //if(Roo.isSafari){ // safari
30946         //    adjust *= 2;
30947        // }
30948         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
30949         if(Roo.isSafari){ // safari
30950             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
30951             v =  (v < 10) ? 10 : v;
30952             v =  (v > 48) ? 48 : v;
30953             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
30954             
30955         }
30956         
30957         
30958         v = Math.max(1, v+adjust);
30959         
30960         this.execCmd('FontSize', v  );
30961     },
30962
30963     onEditorEvent : function(e)
30964     {
30965          
30966         
30967         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
30968             return; // we do not handle this.. (undo manager does..)
30969         }
30970         // in theory this detects if the last element is not a br, then we try and do that.
30971         // its so clicking in space at bottom triggers adding a br and moving the cursor.
30972         if (e &&
30973             e.target.nodeName == 'BODY' &&
30974             e.type == "mouseup" &&
30975             this.doc.body.lastChild
30976            ) {
30977             var lc = this.doc.body.lastChild;
30978             // gtx-trans is google translate plugin adding crap.
30979             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
30980                 lc = lc.previousSibling;
30981             }
30982             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
30983             // if last element is <BR> - then dont do anything.
30984             
30985                 var ns = this.doc.createElement('br');
30986                 this.doc.body.appendChild(ns);
30987                 range = this.doc.createRange();
30988                 range.setStartAfter(ns);
30989                 range.collapse(true);
30990                 var sel = this.win.getSelection();
30991                 sel.removeAllRanges();
30992                 sel.addRange(range);
30993             }
30994         }
30995         
30996         
30997         
30998         this.fireEditorEvent(e);
30999       //  this.updateToolbar();
31000         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31001     },
31002     
31003     fireEditorEvent: function(e)
31004     {
31005         this.owner.fireEvent('editorevent', this, e);
31006     },
31007
31008     insertTag : function(tg)
31009     {
31010         // could be a bit smarter... -> wrap the current selected tRoo..
31011         if (tg.toLowerCase() == 'span' ||
31012             tg.toLowerCase() == 'code' ||
31013             tg.toLowerCase() == 'sup' ||
31014             tg.toLowerCase() == 'sub' 
31015             ) {
31016             
31017             range = this.createRange(this.getSelection());
31018             var wrappingNode = this.doc.createElement(tg.toLowerCase());
31019             wrappingNode.appendChild(range.extractContents());
31020             range.insertNode(wrappingNode);
31021
31022             return;
31023             
31024             
31025             
31026         }
31027         this.execCmd("formatblock",   tg);
31028         this.undoManager.addEvent(); 
31029     },
31030     
31031     insertText : function(txt)
31032     {
31033         
31034         
31035         var range = this.createRange();
31036         range.deleteContents();
31037                //alert(Sender.getAttribute('label'));
31038                
31039         range.insertNode(this.doc.createTextNode(txt));
31040         this.undoManager.addEvent();
31041     } ,
31042     
31043      
31044
31045     /**
31046      * Executes a Midas editor command on the editor document and performs necessary focus and
31047      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31048      * @param {String} cmd The Midas command
31049      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31050      */
31051     relayCmd : function(cmd, value)
31052     {
31053         
31054         switch (cmd) {
31055             case 'justifyleft':
31056             case 'justifyright':
31057             case 'justifycenter':
31058                 // if we are in a cell, then we will adjust the
31059                 var n = this.getParentElement();
31060                 var td = n.closest('td');
31061                 if (td) {
31062                     var bl = Roo.htmleditor.Block.factory(td);
31063                     bl.textAlign = cmd.replace('justify','');
31064                     bl.updateElement();
31065                     this.owner.fireEvent('editorevent', this);
31066                     return;
31067                 }
31068                 this.execCmd('styleWithCSS', true); // 
31069                 break;
31070             case 'bold':
31071             case 'italic':
31072                 // if there is no selection, then we insert, and set the curson inside it..
31073                 this.execCmd('styleWithCSS', false); 
31074                 break;
31075                 
31076         
31077             default:
31078                 break;
31079         }
31080         
31081         
31082         this.win.focus();
31083         this.execCmd(cmd, value);
31084         this.owner.fireEvent('editorevent', this);
31085         //this.updateToolbar();
31086         this.owner.deferFocus();
31087     },
31088
31089     /**
31090      * Executes a Midas editor command directly on the editor document.
31091      * For visual commands, you should use {@link #relayCmd} instead.
31092      * <b>This should only be called after the editor is initialized.</b>
31093      * @param {String} cmd The Midas command
31094      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31095      */
31096     execCmd : function(cmd, value){
31097         this.doc.execCommand(cmd, false, value === undefined ? null : value);
31098         this.syncValue();
31099     },
31100  
31101  
31102    
31103     /**
31104      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31105      * to insert tRoo.
31106      * @param {String} text | dom node.. 
31107      */
31108     insertAtCursor : function(text)
31109     {
31110         
31111         if(!this.activated){
31112             return;
31113         }
31114          
31115         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31116             this.win.focus();
31117             
31118             
31119             // from jquery ui (MIT licenced)
31120             var range, node;
31121             var win = this.win;
31122             
31123             if (win.getSelection && win.getSelection().getRangeAt) {
31124                 
31125                 // delete the existing?
31126                 
31127                 this.createRange(this.getSelection()).deleteContents();
31128                 range = win.getSelection().getRangeAt(0);
31129                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31130                 range.insertNode(node);
31131                 range = range.cloneRange();
31132                 range.collapse(false);
31133                  
31134                 win.getSelection().removeAllRanges();
31135                 win.getSelection().addRange(range);
31136                 
31137                 
31138                 
31139             } else if (win.document.selection && win.document.selection.createRange) {
31140                 // no firefox support
31141                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31142                 win.document.selection.createRange().pasteHTML(txt);
31143             
31144             } else {
31145                 // no firefox support
31146                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31147                 this.execCmd('InsertHTML', txt);
31148             } 
31149             this.syncValue();
31150             
31151             this.deferFocus();
31152         }
31153     },
31154  // private
31155     mozKeyPress : function(e){
31156         if(e.ctrlKey){
31157             var c = e.getCharCode(), cmd;
31158           
31159             if(c > 0){
31160                 c = String.fromCharCode(c).toLowerCase();
31161                 switch(c){
31162                     case 'b':
31163                         cmd = 'bold';
31164                         break;
31165                     case 'i':
31166                         cmd = 'italic';
31167                         break;
31168                     
31169                     case 'u':
31170                         cmd = 'underline';
31171                         break;
31172                     
31173                     //case 'v':
31174                       //  this.cleanUpPaste.defer(100, this);
31175                       //  return;
31176                         
31177                 }
31178                 if(cmd){
31179                     
31180                     this.relayCmd(cmd);
31181                     //this.win.focus();
31182                     //this.execCmd(cmd);
31183                     //this.deferFocus();
31184                     e.preventDefault();
31185                 }
31186                 
31187             }
31188         }
31189     },
31190
31191     // private
31192     fixKeys : function(){ // load time branching for fastest keydown performance
31193         
31194         
31195         if(Roo.isIE){
31196             return function(e){
31197                 var k = e.getKey(), r;
31198                 if(k == e.TAB){
31199                     e.stopEvent();
31200                     r = this.doc.selection.createRange();
31201                     if(r){
31202                         r.collapse(true);
31203                         r.pasteHTML('&#160;&#160;&#160;&#160;');
31204                         this.deferFocus();
31205                     }
31206                     return;
31207                 }
31208                 /// this is handled by Roo.htmleditor.KeyEnter
31209                  /*
31210                 if(k == e.ENTER){
31211                     r = this.doc.selection.createRange();
31212                     if(r){
31213                         var target = r.parentElement();
31214                         if(!target || target.tagName.toLowerCase() != 'li'){
31215                             e.stopEvent();
31216                             r.pasteHTML('<br/>');
31217                             r.collapse(false);
31218                             r.select();
31219                         }
31220                     }
31221                 }
31222                 */
31223                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31224                 //    this.cleanUpPaste.defer(100, this);
31225                 //    return;
31226                 //}
31227                 
31228                 
31229             };
31230         }else if(Roo.isOpera){
31231             return function(e){
31232                 var k = e.getKey();
31233                 if(k == e.TAB){
31234                     e.stopEvent();
31235                     this.win.focus();
31236                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
31237                     this.deferFocus();
31238                 }
31239                
31240                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31241                 //    this.cleanUpPaste.defer(100, this);
31242                  //   return;
31243                 //}
31244                 
31245             };
31246         }else if(Roo.isSafari){
31247             return function(e){
31248                 var k = e.getKey();
31249                 
31250                 if(k == e.TAB){
31251                     e.stopEvent();
31252                     this.execCmd('InsertText','\t');
31253                     this.deferFocus();
31254                     return;
31255                 }
31256                  this.mozKeyPress(e);
31257                 
31258                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31259                  //   this.cleanUpPaste.defer(100, this);
31260                  //   return;
31261                // }
31262                 
31263              };
31264         }
31265     }(),
31266     
31267     getAllAncestors: function()
31268     {
31269         var p = this.getSelectedNode();
31270         var a = [];
31271         if (!p) {
31272             a.push(p); // push blank onto stack..
31273             p = this.getParentElement();
31274         }
31275         
31276         
31277         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31278             a.push(p);
31279             p = p.parentNode;
31280         }
31281         a.push(this.doc.body);
31282         return a;
31283     },
31284     lastSel : false,
31285     lastSelNode : false,
31286     
31287     
31288     getSelection : function() 
31289     {
31290         this.assignDocWin();
31291         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31292     },
31293     /**
31294      * Select a dom node
31295      * @param {DomElement} node the node to select
31296      */
31297     selectNode : function(node, collapse)
31298     {
31299         var nodeRange = node.ownerDocument.createRange();
31300         try {
31301             nodeRange.selectNode(node);
31302         } catch (e) {
31303             nodeRange.selectNodeContents(node);
31304         }
31305         if (collapse === true) {
31306             nodeRange.collapse(true);
31307         }
31308         //
31309         var s = this.win.getSelection();
31310         s.removeAllRanges();
31311         s.addRange(nodeRange);
31312     },
31313     
31314     getSelectedNode: function() 
31315     {
31316         // this may only work on Gecko!!!
31317         
31318         // should we cache this!!!!
31319         
31320          
31321          
31322         var range = this.createRange(this.getSelection()).cloneRange();
31323         
31324         if (Roo.isIE) {
31325             var parent = range.parentElement();
31326             while (true) {
31327                 var testRange = range.duplicate();
31328                 testRange.moveToElementText(parent);
31329                 if (testRange.inRange(range)) {
31330                     break;
31331                 }
31332                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31333                     break;
31334                 }
31335                 parent = parent.parentElement;
31336             }
31337             return parent;
31338         }
31339         
31340         // is ancestor a text element.
31341         var ac =  range.commonAncestorContainer;
31342         if (ac.nodeType == 3) {
31343             ac = ac.parentNode;
31344         }
31345         
31346         var ar = ac.childNodes;
31347          
31348         var nodes = [];
31349         var other_nodes = [];
31350         var has_other_nodes = false;
31351         for (var i=0;i<ar.length;i++) {
31352             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
31353                 continue;
31354             }
31355             // fullly contained node.
31356             
31357             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31358                 nodes.push(ar[i]);
31359                 continue;
31360             }
31361             
31362             // probably selected..
31363             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31364                 other_nodes.push(ar[i]);
31365                 continue;
31366             }
31367             // outer..
31368             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
31369                 continue;
31370             }
31371             
31372             
31373             has_other_nodes = true;
31374         }
31375         if (!nodes.length && other_nodes.length) {
31376             nodes= other_nodes;
31377         }
31378         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
31379             return false;
31380         }
31381         
31382         return nodes[0];
31383     },
31384     
31385     
31386     createRange: function(sel)
31387     {
31388         // this has strange effects when using with 
31389         // top toolbar - not sure if it's a great idea.
31390         //this.editor.contentWindow.focus();
31391         if (typeof sel != "undefined") {
31392             try {
31393                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
31394             } catch(e) {
31395                 return this.doc.createRange();
31396             }
31397         } else {
31398             return this.doc.createRange();
31399         }
31400     },
31401     getParentElement: function()
31402     {
31403         
31404         this.assignDocWin();
31405         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
31406         
31407         var range = this.createRange(sel);
31408          
31409         try {
31410             var p = range.commonAncestorContainer;
31411             while (p.nodeType == 3) { // text node
31412                 p = p.parentNode;
31413             }
31414             return p;
31415         } catch (e) {
31416             return null;
31417         }
31418     
31419     },
31420     /***
31421      *
31422      * Range intersection.. the hard stuff...
31423      *  '-1' = before
31424      *  '0' = hits..
31425      *  '1' = after.
31426      *         [ -- selected range --- ]
31427      *   [fail]                        [fail]
31428      *
31429      *    basically..
31430      *      if end is before start or  hits it. fail.
31431      *      if start is after end or hits it fail.
31432      *
31433      *   if either hits (but other is outside. - then it's not 
31434      *   
31435      *    
31436      **/
31437     
31438     
31439     // @see http://www.thismuchiknow.co.uk/?p=64.
31440     rangeIntersectsNode : function(range, node)
31441     {
31442         var nodeRange = node.ownerDocument.createRange();
31443         try {
31444             nodeRange.selectNode(node);
31445         } catch (e) {
31446             nodeRange.selectNodeContents(node);
31447         }
31448     
31449         var rangeStartRange = range.cloneRange();
31450         rangeStartRange.collapse(true);
31451     
31452         var rangeEndRange = range.cloneRange();
31453         rangeEndRange.collapse(false);
31454     
31455         var nodeStartRange = nodeRange.cloneRange();
31456         nodeStartRange.collapse(true);
31457     
31458         var nodeEndRange = nodeRange.cloneRange();
31459         nodeEndRange.collapse(false);
31460     
31461         return rangeStartRange.compareBoundaryPoints(
31462                  Range.START_TO_START, nodeEndRange) == -1 &&
31463                rangeEndRange.compareBoundaryPoints(
31464                  Range.START_TO_START, nodeStartRange) == 1;
31465         
31466          
31467     },
31468     rangeCompareNode : function(range, node)
31469     {
31470         var nodeRange = node.ownerDocument.createRange();
31471         try {
31472             nodeRange.selectNode(node);
31473         } catch (e) {
31474             nodeRange.selectNodeContents(node);
31475         }
31476         
31477         
31478         range.collapse(true);
31479     
31480         nodeRange.collapse(true);
31481      
31482         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
31483         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
31484          
31485         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
31486         
31487         var nodeIsBefore   =  ss == 1;
31488         var nodeIsAfter    = ee == -1;
31489         
31490         if (nodeIsBefore && nodeIsAfter) {
31491             return 0; // outer
31492         }
31493         if (!nodeIsBefore && nodeIsAfter) {
31494             return 1; //right trailed.
31495         }
31496         
31497         if (nodeIsBefore && !nodeIsAfter) {
31498             return 2;  // left trailed.
31499         }
31500         // fully contined.
31501         return 3;
31502     },
31503  
31504     cleanWordChars : function(input) {// change the chars to hex code
31505         
31506        var swapCodes  = [ 
31507             [    8211, "&#8211;" ], 
31508             [    8212, "&#8212;" ], 
31509             [    8216,  "'" ],  
31510             [    8217, "'" ],  
31511             [    8220, '"' ],  
31512             [    8221, '"' ],  
31513             [    8226, "*" ],  
31514             [    8230, "..." ]
31515         ]; 
31516         var output = input;
31517         Roo.each(swapCodes, function(sw) { 
31518             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
31519             
31520             output = output.replace(swapper, sw[1]);
31521         });
31522         
31523         return output;
31524     },
31525     
31526      
31527     
31528         
31529     
31530     cleanUpChild : function (node)
31531     {
31532         
31533         new Roo.htmleditor.FilterComment({node : node});
31534         new Roo.htmleditor.FilterAttributes({
31535                 node : node,
31536                 attrib_black : this.ablack,
31537                 attrib_clean : this.aclean,
31538                 style_white : this.cwhite,
31539                 style_black : this.cblack
31540         });
31541         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
31542         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
31543          
31544         
31545     },
31546     
31547     /**
31548      * Clean up MS wordisms...
31549      * @deprecated - use filter directly
31550      */
31551     cleanWord : function(node)
31552     {
31553         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
31554         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
31555         
31556     },
31557    
31558     
31559     /**
31560
31561      * @deprecated - use filters
31562      */
31563     cleanTableWidths : function(node)
31564     {
31565         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
31566         
31567  
31568     },
31569     
31570      
31571         
31572     applyBlacklists : function()
31573     {
31574         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
31575         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
31576         
31577         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
31578         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
31579         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
31580         
31581         this.white = [];
31582         this.black = [];
31583         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
31584             if (b.indexOf(tag) > -1) {
31585                 return;
31586             }
31587             this.white.push(tag);
31588             
31589         }, this);
31590         
31591         Roo.each(w, function(tag) {
31592             if (b.indexOf(tag) > -1) {
31593                 return;
31594             }
31595             if (this.white.indexOf(tag) > -1) {
31596                 return;
31597             }
31598             this.white.push(tag);
31599             
31600         }, this);
31601         
31602         
31603         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
31604             if (w.indexOf(tag) > -1) {
31605                 return;
31606             }
31607             this.black.push(tag);
31608             
31609         }, this);
31610         
31611         Roo.each(b, function(tag) {
31612             if (w.indexOf(tag) > -1) {
31613                 return;
31614             }
31615             if (this.black.indexOf(tag) > -1) {
31616                 return;
31617             }
31618             this.black.push(tag);
31619             
31620         }, this);
31621         
31622         
31623         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
31624         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
31625         
31626         this.cwhite = [];
31627         this.cblack = [];
31628         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
31629             if (b.indexOf(tag) > -1) {
31630                 return;
31631             }
31632             this.cwhite.push(tag);
31633             
31634         }, this);
31635         
31636         Roo.each(w, function(tag) {
31637             if (b.indexOf(tag) > -1) {
31638                 return;
31639             }
31640             if (this.cwhite.indexOf(tag) > -1) {
31641                 return;
31642             }
31643             this.cwhite.push(tag);
31644             
31645         }, this);
31646         
31647         
31648         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
31649             if (w.indexOf(tag) > -1) {
31650                 return;
31651             }
31652             this.cblack.push(tag);
31653             
31654         }, this);
31655         
31656         Roo.each(b, function(tag) {
31657             if (w.indexOf(tag) > -1) {
31658                 return;
31659             }
31660             if (this.cblack.indexOf(tag) > -1) {
31661                 return;
31662             }
31663             this.cblack.push(tag);
31664             
31665         }, this);
31666     },
31667     
31668     setStylesheets : function(stylesheets)
31669     {
31670         if(typeof(stylesheets) == 'string'){
31671             Roo.get(this.iframe.contentDocument.head).createChild({
31672                 tag : 'link',
31673                 rel : 'stylesheet',
31674                 type : 'text/css',
31675                 href : stylesheets
31676             });
31677             
31678             return;
31679         }
31680         var _this = this;
31681      
31682         Roo.each(stylesheets, function(s) {
31683             if(!s.length){
31684                 return;
31685             }
31686             
31687             Roo.get(_this.iframe.contentDocument.head).createChild({
31688                 tag : 'link',
31689                 rel : 'stylesheet',
31690                 type : 'text/css',
31691                 href : s
31692             });
31693         });
31694
31695         
31696     },
31697     
31698     
31699     updateLanguage : function()
31700     {
31701         if (!this.iframe || !this.iframe.contentDocument) {
31702             return;
31703         }
31704         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
31705     },
31706     
31707     
31708     removeStylesheets : function()
31709     {
31710         var _this = this;
31711         
31712         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
31713             s.remove();
31714         });
31715     },
31716     
31717     setStyle : function(style)
31718     {
31719         Roo.get(this.iframe.contentDocument.head).createChild({
31720             tag : 'style',
31721             type : 'text/css',
31722             html : style
31723         });
31724
31725         return;
31726     }
31727     
31728     // hide stuff that is not compatible
31729     /**
31730      * @event blur
31731      * @hide
31732      */
31733     /**
31734      * @event change
31735      * @hide
31736      */
31737     /**
31738      * @event focus
31739      * @hide
31740      */
31741     /**
31742      * @event specialkey
31743      * @hide
31744      */
31745     /**
31746      * @cfg {String} fieldClass @hide
31747      */
31748     /**
31749      * @cfg {String} focusClass @hide
31750      */
31751     /**
31752      * @cfg {String} autoCreate @hide
31753      */
31754     /**
31755      * @cfg {String} inputType @hide
31756      */
31757     /**
31758      * @cfg {String} invalidClass @hide
31759      */
31760     /**
31761      * @cfg {String} invalidText @hide
31762      */
31763     /**
31764      * @cfg {String} msgFx @hide
31765      */
31766     /**
31767      * @cfg {String} validateOnBlur @hide
31768      */
31769 });
31770
31771 Roo.HtmlEditorCore.white = [
31772         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
31773         
31774        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
31775        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
31776        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
31777        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
31778        'TABLE',   'UL',         'XMP', 
31779        
31780        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
31781       'THEAD',   'TR', 
31782      
31783       'DIR', 'MENU', 'OL', 'UL', 'DL',
31784        
31785       'EMBED',  'OBJECT'
31786 ];
31787
31788
31789 Roo.HtmlEditorCore.black = [
31790     //    'embed',  'object', // enable - backend responsiblity to clean thiese
31791         'APPLET', // 
31792         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
31793         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
31794         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
31795         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
31796         //'FONT' // CLEAN LATER..
31797         'COLGROUP', 'COL'   // messy tables.
31798         
31799         
31800 ];
31801 Roo.HtmlEditorCore.clean = [ // ?? needed???
31802      'SCRIPT', 'STYLE', 'TITLE', 'XML'
31803 ];
31804 Roo.HtmlEditorCore.tag_remove = [
31805     'FONT', 'TBODY'  
31806 ];
31807 // attributes..
31808
31809 Roo.HtmlEditorCore.ablack = [
31810     'on'
31811 ];
31812     
31813 Roo.HtmlEditorCore.aclean = [ 
31814     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
31815 ];
31816
31817 // protocols..
31818 Roo.HtmlEditorCore.pwhite= [
31819         'http',  'https',  'mailto'
31820 ];
31821
31822 // white listed style attributes.
31823 Roo.HtmlEditorCore.cwhite= [
31824       //  'text-align', /// default is to allow most things..
31825       
31826          
31827 //        'font-size'//??
31828 ];
31829
31830 // black listed style attributes.
31831 Roo.HtmlEditorCore.cblack= [
31832       //  'font-size' -- this can be set by the project 
31833 ];
31834
31835
31836
31837
31838     /*
31839  * - LGPL
31840  *
31841  * HtmlEditor
31842  * 
31843  */
31844
31845 /**
31846  * @class Roo.bootstrap.form.HtmlEditor
31847  * @extends Roo.bootstrap.form.TextArea
31848  * Bootstrap HtmlEditor class
31849
31850  * @constructor
31851  * Create a new HtmlEditor
31852  * @param {Object} config The config object
31853  */
31854
31855 Roo.bootstrap.form.HtmlEditor = function(config){
31856     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
31857     if (!this.toolbars) {
31858         this.toolbars = [];
31859     }
31860     
31861     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
31862     this.addEvents({
31863             /**
31864              * @event initialize
31865              * Fires when the editor is fully initialized (including the iframe)
31866              * @param {HtmlEditor} this
31867              */
31868             initialize: true,
31869             /**
31870              * @event activate
31871              * Fires when the editor is first receives the focus. Any insertion must wait
31872              * until after this event.
31873              * @param {HtmlEditor} this
31874              */
31875             activate: true,
31876              /**
31877              * @event beforesync
31878              * Fires before the textarea is updated with content from the editor iframe. Return false
31879              * to cancel the sync.
31880              * @param {HtmlEditor} this
31881              * @param {String} html
31882              */
31883             beforesync: true,
31884              /**
31885              * @event beforepush
31886              * Fires before the iframe editor is updated with content from the textarea. Return false
31887              * to cancel the push.
31888              * @param {HtmlEditor} this
31889              * @param {String} html
31890              */
31891             beforepush: true,
31892              /**
31893              * @event sync
31894              * Fires when the textarea is updated with content from the editor iframe.
31895              * @param {HtmlEditor} this
31896              * @param {String} html
31897              */
31898             sync: true,
31899              /**
31900              * @event push
31901              * Fires when the iframe editor is updated with content from the textarea.
31902              * @param {HtmlEditor} this
31903              * @param {String} html
31904              */
31905             push: true,
31906              /**
31907              * @event editmodechange
31908              * Fires when the editor switches edit modes
31909              * @param {HtmlEditor} this
31910              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
31911              */
31912             editmodechange: true,
31913             /**
31914              * @event editorevent
31915              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
31916              * @param {HtmlEditor} this
31917              */
31918             editorevent: true,
31919             /**
31920              * @event firstfocus
31921              * Fires when on first focus - needed by toolbars..
31922              * @param {HtmlEditor} this
31923              */
31924             firstfocus: true,
31925             /**
31926              * @event autosave
31927              * Auto save the htmlEditor value as a file into Events
31928              * @param {HtmlEditor} this
31929              */
31930             autosave: true,
31931             /**
31932              * @event savedpreview
31933              * preview the saved version of htmlEditor
31934              * @param {HtmlEditor} this
31935              */
31936             savedpreview: true
31937         });
31938 };
31939
31940
31941 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
31942     
31943     
31944       /**
31945      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
31946      */
31947     toolbars : false,
31948     
31949      /**
31950     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
31951     */
31952     btns : [],
31953    
31954      /**
31955      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
31956      *                        Roo.resizable.
31957      */
31958     resizable : false,
31959      /**
31960      * @cfg {Number} height (in pixels)
31961      */   
31962     height: 300,
31963    /**
31964      * @cfg {Number} width (in pixels)
31965      */   
31966     width: false,
31967     
31968     /**
31969      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
31970      * 
31971      */
31972     stylesheets: false,
31973     
31974     // id of frame..
31975     frameId: false,
31976     
31977     // private properties
31978     validationEvent : false,
31979     deferHeight: true,
31980     initialized : false,
31981     activated : false,
31982     
31983     onFocus : Roo.emptyFn,
31984     iframePad:3,
31985     hideMode:'offsets',
31986     
31987     tbContainer : false,
31988     
31989     bodyCls : '',
31990     
31991     toolbarContainer :function() {
31992         return this.wrap.select('.x-html-editor-tb',true).first();
31993     },
31994
31995     /**
31996      * Protected method that will not generally be called directly. It
31997      * is called when the editor creates its toolbar. Override this method if you need to
31998      * add custom toolbar buttons.
31999      * @param {HtmlEditor} editor
32000      */
32001     createToolbar : function(){
32002         Roo.log('renewing');
32003         Roo.log("create toolbars");
32004         
32005         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
32006         this.toolbars[0].render(this.toolbarContainer());
32007         
32008         return;
32009         
32010 //        if (!editor.toolbars || !editor.toolbars.length) {
32011 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
32012 //        }
32013 //        
32014 //        for (var i =0 ; i < editor.toolbars.length;i++) {
32015 //            editor.toolbars[i] = Roo.factory(
32016 //                    typeof(editor.toolbars[i]) == 'string' ?
32017 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
32018 //                Roo.bootstrap.form.HtmlEditor);
32019 //            editor.toolbars[i].init(editor);
32020 //        }
32021     },
32022
32023      
32024     // private
32025     onRender : function(ct, position)
32026     {
32027        // Roo.log("Call onRender: " + this.xtype);
32028         var _t = this;
32029         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32030       
32031         this.wrap = this.inputEl().wrap({
32032             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32033         });
32034         
32035         this.editorcore.onRender(ct, position);
32036          
32037         if (this.resizable) {
32038             this.resizeEl = new Roo.Resizable(this.wrap, {
32039                 pinned : true,
32040                 wrap: true,
32041                 dynamic : true,
32042                 minHeight : this.height,
32043                 height: this.height,
32044                 handles : this.resizable,
32045                 width: this.width,
32046                 listeners : {
32047                     resize : function(r, w, h) {
32048                         _t.onResize(w,h); // -something
32049                     }
32050                 }
32051             });
32052             
32053         }
32054         this.createToolbar(this);
32055        
32056         
32057         if(!this.width && this.resizable){
32058             this.setSize(this.wrap.getSize());
32059         }
32060         if (this.resizeEl) {
32061             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
32062             // should trigger onReize..
32063         }
32064         
32065     },
32066
32067     // private
32068     onResize : function(w, h)
32069     {
32070         Roo.log('resize: ' +w + ',' + h );
32071         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32072         var ew = false;
32073         var eh = false;
32074         
32075         if(this.inputEl() ){
32076             if(typeof w == 'number'){
32077                 var aw = w - this.wrap.getFrameWidth('lr');
32078                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32079                 ew = aw;
32080             }
32081             if(typeof h == 'number'){
32082                  var tbh = -11;  // fixme it needs to tool bar size!
32083                 for (var i =0; i < this.toolbars.length;i++) {
32084                     // fixme - ask toolbars for heights?
32085                     tbh += this.toolbars[i].el.getHeight();
32086                     //if (this.toolbars[i].footer) {
32087                     //    tbh += this.toolbars[i].footer.el.getHeight();
32088                     //}
32089                 }
32090               
32091                 
32092                 
32093                 
32094                 
32095                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32096                 ah -= 5; // knock a few pixes off for look..
32097                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32098                 var eh = ah;
32099             }
32100         }
32101         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32102         this.editorcore.onResize(ew,eh);
32103         
32104     },
32105
32106     /**
32107      * Toggles the editor between standard and source edit mode.
32108      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32109      */
32110     toggleSourceEdit : function(sourceEditMode)
32111     {
32112         this.editorcore.toggleSourceEdit(sourceEditMode);
32113         
32114         if(this.editorcore.sourceEditMode){
32115             Roo.log('editor - showing textarea');
32116             
32117 //            Roo.log('in');
32118 //            Roo.log(this.syncValue());
32119             this.syncValue();
32120             this.inputEl().removeClass(['hide', 'x-hidden']);
32121             this.inputEl().dom.removeAttribute('tabIndex');
32122             this.inputEl().focus();
32123         }else{
32124             Roo.log('editor - hiding textarea');
32125 //            Roo.log('out')
32126 //            Roo.log(this.pushValue()); 
32127             this.pushValue();
32128             
32129             this.inputEl().addClass(['hide', 'x-hidden']);
32130             this.inputEl().dom.setAttribute('tabIndex', -1);
32131             //this.deferFocus();
32132         }
32133          
32134         if(this.resizable){
32135             this.setSize(this.wrap.getSize());
32136         }
32137         
32138         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32139     },
32140  
32141     // private (for BoxComponent)
32142     adjustSize : Roo.BoxComponent.prototype.adjustSize,
32143
32144     // private (for BoxComponent)
32145     getResizeEl : function(){
32146         return this.wrap;
32147     },
32148
32149     // private (for BoxComponent)
32150     getPositionEl : function(){
32151         return this.wrap;
32152     },
32153
32154     // private
32155     initEvents : function(){
32156         this.originalValue = this.getValue();
32157     },
32158
32159 //    /**
32160 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32161 //     * @method
32162 //     */
32163 //    markInvalid : Roo.emptyFn,
32164 //    /**
32165 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32166 //     * @method
32167 //     */
32168 //    clearInvalid : Roo.emptyFn,
32169
32170     setValue : function(v){
32171         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32172         this.editorcore.pushValue();
32173     },
32174
32175      
32176     // private
32177     deferFocus : function(){
32178         this.focus.defer(10, this);
32179     },
32180
32181     // doc'ed in Field
32182     focus : function(){
32183         this.editorcore.focus();
32184         
32185     },
32186       
32187
32188     // private
32189     onDestroy : function(){
32190         
32191         
32192         
32193         if(this.rendered){
32194             
32195             for (var i =0; i < this.toolbars.length;i++) {
32196                 // fixme - ask toolbars for heights?
32197                 this.toolbars[i].onDestroy();
32198             }
32199             
32200             this.wrap.dom.innerHTML = '';
32201             this.wrap.remove();
32202         }
32203     },
32204
32205     // private
32206     onFirstFocus : function(){
32207         //Roo.log("onFirstFocus");
32208         this.editorcore.onFirstFocus();
32209          for (var i =0; i < this.toolbars.length;i++) {
32210             this.toolbars[i].onFirstFocus();
32211         }
32212         
32213     },
32214     
32215     // private
32216     syncValue : function()
32217     {   
32218         this.editorcore.syncValue();
32219     },
32220     
32221     pushValue : function()
32222     {   
32223         this.editorcore.pushValue();
32224     }
32225      
32226     
32227     // hide stuff that is not compatible
32228     /**
32229      * @event blur
32230      * @hide
32231      */
32232     /**
32233      * @event change
32234      * @hide
32235      */
32236     /**
32237      * @event focus
32238      * @hide
32239      */
32240     /**
32241      * @event specialkey
32242      * @hide
32243      */
32244     /**
32245      * @cfg {String} fieldClass @hide
32246      */
32247     /**
32248      * @cfg {String} focusClass @hide
32249      */
32250     /**
32251      * @cfg {String} autoCreate @hide
32252      */
32253     /**
32254      * @cfg {String} inputType @hide
32255      */
32256      
32257     /**
32258      * @cfg {String} invalidText @hide
32259      */
32260     /**
32261      * @cfg {String} msgFx @hide
32262      */
32263     /**
32264      * @cfg {String} validateOnBlur @hide
32265      */
32266 });
32267  
32268     
32269    
32270    
32271    
32272       
32273 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
32274 /**
32275  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
32276  * @parent Roo.bootstrap.form.HtmlEditor
32277  * @extends Roo.bootstrap.nav.Simplebar
32278  * Basic Toolbar
32279  * 
32280  * @example
32281  * Usage:
32282  *
32283  new Roo.bootstrap.form.HtmlEditor({
32284     ....
32285     toolbars : [
32286         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
32287             disable : { fonts: 1 , format: 1, ..., ... , ...],
32288             btns : [ .... ]
32289         })
32290     }
32291      
32292  * 
32293  * @cfg {Object} disable List of elements to disable..
32294  * @cfg {Array} btns List of additional buttons.
32295  * 
32296  * 
32297  * NEEDS Extra CSS? 
32298  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32299  */
32300  
32301 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
32302 {
32303     
32304     Roo.apply(this, config);
32305     
32306     // default disabled, based on 'good practice'..
32307     this.disable = this.disable || {};
32308     Roo.applyIf(this.disable, {
32309         fontSize : true,
32310         colors : true,
32311         specialElements : true
32312     });
32313     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
32314     
32315     this.editor = config.editor;
32316     this.editorcore = config.editor.editorcore;
32317     
32318     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
32319     
32320     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32321     // dont call parent... till later.
32322 }
32323 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
32324      
32325     bar : true,
32326     
32327     editor : false,
32328     editorcore : false,
32329     
32330     
32331     formats : [
32332         "p" ,  
32333         "h1","h2","h3","h4","h5","h6", 
32334         "pre", "code", 
32335         "abbr", "acronym", "address", "cite", "samp", "var",
32336         'div','span'
32337     ],
32338     
32339     onRender : function(ct, position)
32340     {
32341        // Roo.log("Call onRender: " + this.xtype);
32342         
32343        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
32344        Roo.log(this.el);
32345        this.el.dom.style.marginBottom = '0';
32346        var _this = this;
32347        var editorcore = this.editorcore;
32348        var editor= this.editor;
32349        
32350        var children = [];
32351        var btn = function(id,cmd , toggle, handler, html){
32352        
32353             var  event = toggle ? 'toggle' : 'click';
32354        
32355             var a = {
32356                 size : 'sm',
32357                 xtype: 'Button',
32358                 xns: Roo.bootstrap,
32359                 //glyphicon : id,
32360                 fa: id,
32361                 cmd : id || cmd,
32362                 enableToggle:toggle !== false,
32363                 html : html || '',
32364                 pressed : toggle ? false : null,
32365                 listeners : {}
32366             };
32367             a.listeners[toggle ? 'toggle' : 'click'] = function() {
32368                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
32369             };
32370             children.push(a);
32371             return a;
32372        }
32373        
32374     //    var cb_box = function...
32375         
32376         var style = {
32377                 xtype: 'Button',
32378                 size : 'sm',
32379                 xns: Roo.bootstrap,
32380                 fa : 'font',
32381                 //html : 'submit'
32382                 menu : {
32383                     xtype: 'Menu',
32384                     xns: Roo.bootstrap,
32385                     items:  []
32386                 }
32387         };
32388         Roo.each(this.formats, function(f) {
32389             style.menu.items.push({
32390                 xtype :'MenuItem',
32391                 xns: Roo.bootstrap,
32392                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
32393                 tagname : f,
32394                 listeners : {
32395                     click : function()
32396                     {
32397                         editorcore.insertTag(this.tagname);
32398                         editor.focus();
32399                     }
32400                 }
32401                 
32402             });
32403         });
32404         children.push(style);   
32405         
32406         btn('bold',false,true);
32407         btn('italic',false,true);
32408         btn('align-left', 'justifyleft',true);
32409         btn('align-center', 'justifycenter',true);
32410         btn('align-right' , 'justifyright',true);
32411         btn('link', false, false, function(btn) {
32412             //Roo.log("create link?");
32413             var url = prompt(this.createLinkText, this.defaultLinkValue);
32414             if(url && url != 'http:/'+'/'){
32415                 this.editorcore.relayCmd('createlink', url);
32416             }
32417         }),
32418         btn('list','insertunorderedlist',true);
32419         btn('pencil', false,true, function(btn){
32420                 Roo.log(this);
32421                 this.toggleSourceEdit(btn.pressed);
32422         });
32423         
32424         if (this.editor.btns.length > 0) {
32425             for (var i = 0; i<this.editor.btns.length; i++) {
32426                 children.push(this.editor.btns[i]);
32427             }
32428         }
32429         
32430         /*
32431         var cog = {
32432                 xtype: 'Button',
32433                 size : 'sm',
32434                 xns: Roo.bootstrap,
32435                 glyphicon : 'cog',
32436                 //html : 'submit'
32437                 menu : {
32438                     xtype: 'Menu',
32439                     xns: Roo.bootstrap,
32440                     items:  []
32441                 }
32442         };
32443         
32444         cog.menu.items.push({
32445             xtype :'MenuItem',
32446             xns: Roo.bootstrap,
32447             html : Clean styles,
32448             tagname : f,
32449             listeners : {
32450                 click : function()
32451                 {
32452                     editorcore.insertTag(this.tagname);
32453                     editor.focus();
32454                 }
32455             }
32456             
32457         });
32458        */
32459         
32460          
32461        this.xtype = 'NavSimplebar';
32462         
32463         for(var i=0;i< children.length;i++) {
32464             
32465             this.buttons.add(this.addxtypeChild(children[i]));
32466             
32467         }
32468         
32469         editor.on('editorevent', this.updateToolbar, this);
32470     },
32471     onBtnClick : function(id)
32472     {
32473        this.editorcore.relayCmd(id);
32474        this.editorcore.focus();
32475     },
32476     
32477     /**
32478      * Protected method that will not generally be called directly. It triggers
32479      * a toolbar update by reading the markup state of the current selection in the editor.
32480      */
32481     updateToolbar: function(){
32482
32483         if(!this.editorcore.activated){
32484             this.editor.onFirstFocus(); // is this neeed?
32485             return;
32486         }
32487
32488         var btns = this.buttons; 
32489         var doc = this.editorcore.doc;
32490         btns.get('bold').setActive(doc.queryCommandState('bold'));
32491         btns.get('italic').setActive(doc.queryCommandState('italic'));
32492         //btns.get('underline').setActive(doc.queryCommandState('underline'));
32493         
32494         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
32495         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
32496         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
32497         
32498         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
32499         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
32500          /*
32501         
32502         var ans = this.editorcore.getAllAncestors();
32503         if (this.formatCombo) {
32504             
32505             
32506             var store = this.formatCombo.store;
32507             this.formatCombo.setValue("");
32508             for (var i =0; i < ans.length;i++) {
32509                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
32510                     // select it..
32511                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
32512                     break;
32513                 }
32514             }
32515         }
32516         
32517         
32518         
32519         // hides menus... - so this cant be on a menu...
32520         Roo.bootstrap.MenuMgr.hideAll();
32521         */
32522         Roo.bootstrap.menu.Manager.hideAll();
32523         //this.editorsyncValue();
32524     },
32525     onFirstFocus: function() {
32526         this.buttons.each(function(item){
32527            item.enable();
32528         });
32529     },
32530     toggleSourceEdit : function(sourceEditMode){
32531         
32532           
32533         if(sourceEditMode){
32534             Roo.log("disabling buttons");
32535            this.buttons.each( function(item){
32536                 if(item.cmd != 'pencil'){
32537                     item.disable();
32538                 }
32539             });
32540           
32541         }else{
32542             Roo.log("enabling buttons");
32543             if(this.editorcore.initialized){
32544                 this.buttons.each( function(item){
32545                     item.enable();
32546                 });
32547             }
32548             
32549         }
32550         Roo.log("calling toggole on editor");
32551         // tell the editor that it's been pressed..
32552         this.editor.toggleSourceEdit(sourceEditMode);
32553        
32554     }
32555 });
32556
32557
32558
32559
32560  
32561 /*
32562  * - LGPL
32563  */
32564
32565 /**
32566  * @class Roo.bootstrap.form.Markdown
32567  * @extends Roo.bootstrap.form.TextArea
32568  * Bootstrap Showdown editable area
32569  * @cfg {string} content
32570  * 
32571  * @constructor
32572  * Create a new Showdown
32573  */
32574
32575 Roo.bootstrap.form.Markdown = function(config){
32576     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
32577    
32578 };
32579
32580 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
32581     
32582     editing :false,
32583     
32584     initEvents : function()
32585     {
32586         
32587         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
32588         this.markdownEl = this.el.createChild({
32589             cls : 'roo-markdown-area'
32590         });
32591         this.inputEl().addClass('d-none');
32592         if (this.getValue() == '') {
32593             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
32594             
32595         } else {
32596             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
32597         }
32598         this.markdownEl.on('click', this.toggleTextEdit, this);
32599         this.on('blur', this.toggleTextEdit, this);
32600         this.on('specialkey', this.resizeTextArea, this);
32601     },
32602     
32603     toggleTextEdit : function()
32604     {
32605         var sh = this.markdownEl.getHeight();
32606         this.inputEl().addClass('d-none');
32607         this.markdownEl.addClass('d-none');
32608         if (!this.editing) {
32609             // show editor?
32610             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
32611             this.inputEl().removeClass('d-none');
32612             this.inputEl().focus();
32613             this.editing = true;
32614             return;
32615         }
32616         // show showdown...
32617         this.updateMarkdown();
32618         this.markdownEl.removeClass('d-none');
32619         this.editing = false;
32620         return;
32621     },
32622     updateMarkdown : function()
32623     {
32624         if (this.getValue() == '') {
32625             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
32626             return;
32627         }
32628  
32629         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
32630     },
32631     
32632     resizeTextArea: function () {
32633         
32634         var sh = 100;
32635         Roo.log([sh, this.getValue().split("\n").length * 30]);
32636         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
32637     },
32638     setValue : function(val)
32639     {
32640         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
32641         if (!this.editing) {
32642             this.updateMarkdown();
32643         }
32644         
32645     },
32646     focus : function()
32647     {
32648         if (!this.editing) {
32649             this.toggleTextEdit();
32650         }
32651         
32652     }
32653
32654
32655 });/*
32656  * Based on:
32657  * Ext JS Library 1.1.1
32658  * Copyright(c) 2006-2007, Ext JS, LLC.
32659  *
32660  * Originally Released Under LGPL - original licence link has changed is not relivant.
32661  *
32662  * Fork - LGPL
32663  * <script type="text/javascript">
32664  */
32665  
32666 /**
32667  * @class Roo.bootstrap.PagingToolbar
32668  * @extends Roo.bootstrap.nav.Simplebar
32669  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
32670  * @constructor
32671  * Create a new PagingToolbar
32672  * @param {Object} config The config object
32673  * @param {Roo.data.Store} store
32674  */
32675 Roo.bootstrap.PagingToolbar = function(config)
32676 {
32677     // old args format still supported... - xtype is prefered..
32678         // created from xtype...
32679     
32680     this.ds = config.dataSource;
32681     
32682     if (config.store && !this.ds) {
32683         this.store= Roo.factory(config.store, Roo.data);
32684         this.ds = this.store;
32685         this.ds.xmodule = this.xmodule || false;
32686     }
32687     
32688     this.toolbarItems = [];
32689     if (config.items) {
32690         this.toolbarItems = config.items;
32691     }
32692     
32693     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
32694     
32695     this.cursor = 0;
32696     
32697     if (this.ds) { 
32698         this.bind(this.ds);
32699     }
32700     
32701     if (Roo.bootstrap.version == 4) {
32702         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
32703     } else {
32704         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
32705     }
32706     
32707 };
32708
32709 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
32710     /**
32711      * @cfg {Roo.bootstrap.Button} buttons[]
32712      * Buttons for the toolbar
32713      */
32714      /**
32715      * @cfg {Roo.data.Store} store
32716      * The underlying data store providing the paged data
32717      */
32718     /**
32719      * @cfg {String/HTMLElement/Element} container
32720      * container The id or element that will contain the toolbar
32721      */
32722     /**
32723      * @cfg {Boolean} displayInfo
32724      * True to display the displayMsg (defaults to false)
32725      */
32726     /**
32727      * @cfg {Number} pageSize
32728      * The number of records to display per page (defaults to 20)
32729      */
32730     pageSize: 20,
32731     /**
32732      * @cfg {String} displayMsg
32733      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32734      */
32735     displayMsg : 'Displaying {0} - {1} of {2}',
32736     /**
32737      * @cfg {String} emptyMsg
32738      * The message to display when no records are found (defaults to "No data to display")
32739      */
32740     emptyMsg : 'No data to display',
32741     /**
32742      * Customizable piece of the default paging text (defaults to "Page")
32743      * @type String
32744      */
32745     beforePageText : "Page",
32746     /**
32747      * Customizable piece of the default paging text (defaults to "of %0")
32748      * @type String
32749      */
32750     afterPageText : "of {0}",
32751     /**
32752      * Customizable piece of the default paging text (defaults to "First Page")
32753      * @type String
32754      */
32755     firstText : "First Page",
32756     /**
32757      * Customizable piece of the default paging text (defaults to "Previous Page")
32758      * @type String
32759      */
32760     prevText : "Previous Page",
32761     /**
32762      * Customizable piece of the default paging text (defaults to "Next Page")
32763      * @type String
32764      */
32765     nextText : "Next Page",
32766     /**
32767      * Customizable piece of the default paging text (defaults to "Last Page")
32768      * @type String
32769      */
32770     lastText : "Last Page",
32771     /**
32772      * Customizable piece of the default paging text (defaults to "Refresh")
32773      * @type String
32774      */
32775     refreshText : "Refresh",
32776
32777     buttons : false,
32778     // private
32779     onRender : function(ct, position) 
32780     {
32781         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
32782         this.navgroup.parentId = this.id;
32783         this.navgroup.onRender(this.el, null);
32784         // add the buttons to the navgroup
32785         
32786         if(this.displayInfo){
32787             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
32788             this.displayEl = this.el.select('.x-paging-info', true).first();
32789 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
32790 //            this.displayEl = navel.el.select('span',true).first();
32791         }
32792         
32793         var _this = this;
32794         
32795         if(this.buttons){
32796             Roo.each(_this.buttons, function(e){ // this might need to use render????
32797                Roo.factory(e).render(_this.el);
32798             });
32799         }
32800             
32801         Roo.each(_this.toolbarItems, function(e) {
32802             _this.navgroup.addItem(e);
32803         });
32804         
32805         
32806         this.first = this.navgroup.addItem({
32807             tooltip: this.firstText,
32808             cls: "prev btn-outline-secondary",
32809             html : ' <i class="fa fa-step-backward"></i>',
32810             disabled: true,
32811             preventDefault: true,
32812             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
32813         });
32814         
32815         this.prev =  this.navgroup.addItem({
32816             tooltip: this.prevText,
32817             cls: "prev btn-outline-secondary",
32818             html : ' <i class="fa fa-backward"></i>',
32819             disabled: true,
32820             preventDefault: true,
32821             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
32822         });
32823     //this.addSeparator();
32824         
32825         
32826         var field = this.navgroup.addItem( {
32827             tagtype : 'span',
32828             cls : 'x-paging-position  btn-outline-secondary',
32829              disabled: true,
32830             html : this.beforePageText  +
32831                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
32832                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
32833          } ); //?? escaped?
32834         
32835         this.field = field.el.select('input', true).first();
32836         this.field.on("keydown", this.onPagingKeydown, this);
32837         this.field.on("focus", function(){this.dom.select();});
32838     
32839     
32840         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
32841         //this.field.setHeight(18);
32842         //this.addSeparator();
32843         this.next = this.navgroup.addItem({
32844             tooltip: this.nextText,
32845             cls: "next btn-outline-secondary",
32846             html : ' <i class="fa fa-forward"></i>',
32847             disabled: true,
32848             preventDefault: true,
32849             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
32850         });
32851         this.last = this.navgroup.addItem({
32852             tooltip: this.lastText,
32853             html : ' <i class="fa fa-step-forward"></i>',
32854             cls: "next btn-outline-secondary",
32855             disabled: true,
32856             preventDefault: true,
32857             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
32858         });
32859     //this.addSeparator();
32860         this.loading = this.navgroup.addItem({
32861             tooltip: this.refreshText,
32862             cls: "btn-outline-secondary",
32863             html : ' <i class="fa fa-refresh"></i>',
32864             preventDefault: true,
32865             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
32866         });
32867         
32868     },
32869
32870     // private
32871     updateInfo : function(){
32872         if(this.displayEl){
32873             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
32874             var msg = count == 0 ?
32875                 this.emptyMsg :
32876                 String.format(
32877                     this.displayMsg,
32878                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
32879                 );
32880             this.displayEl.update(msg);
32881         }
32882     },
32883
32884     // private
32885     onLoad : function(ds, r, o)
32886     {
32887         this.cursor = o.params && o.params.start ? o.params.start : 0;
32888         
32889         var d = this.getPageData(),
32890             ap = d.activePage,
32891             ps = d.pages;
32892         
32893         
32894         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
32895         this.field.dom.value = ap;
32896         this.first.setDisabled(ap == 1);
32897         this.prev.setDisabled(ap == 1);
32898         this.next.setDisabled(ap == ps);
32899         this.last.setDisabled(ap == ps);
32900         this.loading.enable();
32901         this.updateInfo();
32902     },
32903
32904     // private
32905     getPageData : function(){
32906         var total = this.ds.getTotalCount();
32907         return {
32908             total : total,
32909             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32910             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32911         };
32912     },
32913
32914     // private
32915     onLoadError : function(proxy, o){
32916         this.loading.enable();
32917         if (this.ds.events.loadexception.listeners.length  < 2) {
32918             // nothing has been assigned to loadexception except this...
32919             // so 
32920             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
32921
32922         }
32923     },
32924
32925     // private
32926     onPagingKeydown : function(e){
32927         var k = e.getKey();
32928         var d = this.getPageData();
32929         if(k == e.RETURN){
32930             var v = this.field.dom.value, pageNum;
32931             if(!v || isNaN(pageNum = parseInt(v, 10))){
32932                 this.field.dom.value = d.activePage;
32933                 return;
32934             }
32935             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32936             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32937             e.stopEvent();
32938         }
32939         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))
32940         {
32941           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32942           this.field.dom.value = pageNum;
32943           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32944           e.stopEvent();
32945         }
32946         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32947         {
32948           var v = this.field.dom.value, pageNum; 
32949           var increment = (e.shiftKey) ? 10 : 1;
32950           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
32951                 increment *= -1;
32952           }
32953           if(!v || isNaN(pageNum = parseInt(v, 10))) {
32954             this.field.dom.value = d.activePage;
32955             return;
32956           }
32957           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
32958           {
32959             this.field.dom.value = parseInt(v, 10) + increment;
32960             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
32961             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32962           }
32963           e.stopEvent();
32964         }
32965     },
32966
32967     // private
32968     beforeLoad : function(){
32969         if(this.loading){
32970             this.loading.disable();
32971         }
32972     },
32973
32974     // private
32975     onClick : function(which){
32976         
32977         var ds = this.ds;
32978         if (!ds) {
32979             return;
32980         }
32981         
32982         switch(which){
32983             case "first":
32984                 ds.load({params:{start: 0, limit: this.pageSize}});
32985             break;
32986             case "prev":
32987                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
32988             break;
32989             case "next":
32990                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
32991             break;
32992             case "last":
32993                 var total = ds.getTotalCount();
32994                 var extra = total % this.pageSize;
32995                 var lastStart = extra ? (total - extra) : total-this.pageSize;
32996                 ds.load({params:{start: lastStart, limit: this.pageSize}});
32997             break;
32998             case "refresh":
32999                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33000             break;
33001         }
33002     },
33003
33004     /**
33005      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33006      * @param {Roo.data.Store} store The data store to unbind
33007      */
33008     unbind : function(ds){
33009         ds.un("beforeload", this.beforeLoad, this);
33010         ds.un("load", this.onLoad, this);
33011         ds.un("loadexception", this.onLoadError, this);
33012         ds.un("remove", this.updateInfo, this);
33013         ds.un("add", this.updateInfo, this);
33014         this.ds = undefined;
33015     },
33016
33017     /**
33018      * Binds the paging toolbar to the specified {@link Roo.data.Store}
33019      * @param {Roo.data.Store} store The data store to bind
33020      */
33021     bind : function(ds){
33022         ds.on("beforeload", this.beforeLoad, this);
33023         ds.on("load", this.onLoad, this);
33024         ds.on("loadexception", this.onLoadError, this);
33025         ds.on("remove", this.updateInfo, this);
33026         ds.on("add", this.updateInfo, this);
33027         this.ds = ds;
33028     }
33029 });/*
33030  * - LGPL
33031  *
33032  * element
33033  * 
33034  */
33035
33036 /**
33037  * @class Roo.bootstrap.MessageBar
33038  * @extends Roo.bootstrap.Component
33039  * Bootstrap MessageBar class
33040  * @cfg {String} html contents of the MessageBar
33041  * @cfg {String} weight (info | success | warning | danger) default info
33042  * @cfg {String} beforeClass insert the bar before the given class
33043  * @cfg {Boolean} closable (true | false) default false
33044  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33045  * 
33046  * @constructor
33047  * Create a new Element
33048  * @param {Object} config The config object
33049  */
33050
33051 Roo.bootstrap.MessageBar = function(config){
33052     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33053 };
33054
33055 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
33056     
33057     html: '',
33058     weight: 'info',
33059     closable: false,
33060     fixed: false,
33061     beforeClass: 'bootstrap-sticky-wrap',
33062     
33063     getAutoCreate : function(){
33064         
33065         var cfg = {
33066             tag: 'div',
33067             cls: 'alert alert-dismissable alert-' + this.weight,
33068             cn: [
33069                 {
33070                     tag: 'span',
33071                     cls: 'message',
33072                     html: this.html || ''
33073                 }
33074             ]
33075         };
33076         
33077         if(this.fixed){
33078             cfg.cls += ' alert-messages-fixed';
33079         }
33080         
33081         if(this.closable){
33082             cfg.cn.push({
33083                 tag: 'button',
33084                 cls: 'close',
33085                 html: 'x'
33086             });
33087         }
33088         
33089         return cfg;
33090     },
33091     
33092     onRender : function(ct, position)
33093     {
33094         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33095         
33096         if(!this.el){
33097             var cfg = Roo.apply({},  this.getAutoCreate());
33098             cfg.id = Roo.id();
33099             
33100             if (this.cls) {
33101                 cfg.cls += ' ' + this.cls;
33102             }
33103             if (this.style) {
33104                 cfg.style = this.style;
33105             }
33106             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33107             
33108             this.el.setVisibilityMode(Roo.Element.DISPLAY);
33109         }
33110         
33111         this.el.select('>button.close').on('click', this.hide, this);
33112         
33113     },
33114     
33115     show : function()
33116     {
33117         if (!this.rendered) {
33118             this.render();
33119         }
33120         
33121         this.el.show();
33122         
33123         this.fireEvent('show', this);
33124         
33125     },
33126     
33127     hide : function()
33128     {
33129         if (!this.rendered) {
33130             this.render();
33131         }
33132         
33133         this.el.hide();
33134         
33135         this.fireEvent('hide', this);
33136     },
33137     
33138     update : function()
33139     {
33140 //        var e = this.el.dom.firstChild;
33141 //        
33142 //        if(this.closable){
33143 //            e = e.nextSibling;
33144 //        }
33145 //        
33146 //        e.data = this.html || '';
33147
33148         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
33149     }
33150    
33151 });
33152
33153  
33154
33155      /*
33156  * - LGPL
33157  *
33158  * Graph
33159  * 
33160  */
33161
33162
33163 /**
33164  * @class Roo.bootstrap.Graph
33165  * @extends Roo.bootstrap.Component
33166  * Bootstrap Graph class
33167 > Prameters
33168  -sm {number} sm 4
33169  -md {number} md 5
33170  @cfg {String} graphtype  bar | vbar | pie
33171  @cfg {number} g_x coodinator | centre x (pie)
33172  @cfg {number} g_y coodinator | centre y (pie)
33173  @cfg {number} g_r radius (pie)
33174  @cfg {number} g_height height of the chart (respected by all elements in the set)
33175  @cfg {number} g_width width of the chart (respected by all elements in the set)
33176  @cfg {Object} title The title of the chart
33177     
33178  -{Array}  values
33179  -opts (object) options for the chart 
33180      o {
33181      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
33182      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
33183      o vgutter (number)
33184      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.
33185      o stacked (boolean) whether or not to tread values as in a stacked bar chart
33186      o to
33187      o stretch (boolean)
33188      o }
33189  -opts (object) options for the pie
33190      o{
33191      o cut
33192      o startAngle (number)
33193      o endAngle (number)
33194      } 
33195  *
33196  * @constructor
33197  * Create a new Input
33198  * @param {Object} config The config object
33199  */
33200
33201 Roo.bootstrap.Graph = function(config){
33202     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
33203     
33204     this.addEvents({
33205         // img events
33206         /**
33207          * @event click
33208          * The img click event for the img.
33209          * @param {Roo.EventObject} e
33210          */
33211         "click" : true
33212     });
33213 };
33214
33215 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
33216     
33217     sm: 4,
33218     md: 5,
33219     graphtype: 'bar',
33220     g_height: 250,
33221     g_width: 400,
33222     g_x: 50,
33223     g_y: 50,
33224     g_r: 30,
33225     opts:{
33226         //g_colors: this.colors,
33227         g_type: 'soft',
33228         g_gutter: '20%'
33229
33230     },
33231     title : false,
33232
33233     getAutoCreate : function(){
33234         
33235         var cfg = {
33236             tag: 'div',
33237             html : null
33238         };
33239         
33240         
33241         return  cfg;
33242     },
33243
33244     onRender : function(ct,position){
33245         
33246         
33247         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
33248         
33249         if (typeof(Raphael) == 'undefined') {
33250             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
33251             return;
33252         }
33253         
33254         this.raphael = Raphael(this.el.dom);
33255         
33256                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33257                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33258                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33259                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
33260                 /*
33261                 r.text(160, 10, "Single Series Chart").attr(txtattr);
33262                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
33263                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
33264                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
33265                 
33266                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
33267                 r.barchart(330, 10, 300, 220, data1);
33268                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
33269                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
33270                 */
33271                 
33272                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33273                 // r.barchart(30, 30, 560, 250,  xdata, {
33274                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
33275                 //     axis : "0 0 1 1",
33276                 //     axisxlabels :  xdata
33277                 //     //yvalues : cols,
33278                    
33279                 // });
33280 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33281 //        
33282 //        this.load(null,xdata,{
33283 //                axis : "0 0 1 1",
33284 //                axisxlabels :  xdata
33285 //                });
33286
33287     },
33288
33289     load : function(graphtype,xdata,opts)
33290     {
33291         this.raphael.clear();
33292         if(!graphtype) {
33293             graphtype = this.graphtype;
33294         }
33295         if(!opts){
33296             opts = this.opts;
33297         }
33298         var r = this.raphael,
33299             fin = function () {
33300                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
33301             },
33302             fout = function () {
33303                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
33304             },
33305             pfin = function() {
33306                 this.sector.stop();
33307                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
33308
33309                 if (this.label) {
33310                     this.label[0].stop();
33311                     this.label[0].attr({ r: 7.5 });
33312                     this.label[1].attr({ "font-weight": 800 });
33313                 }
33314             },
33315             pfout = function() {
33316                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
33317
33318                 if (this.label) {
33319                     this.label[0].animate({ r: 5 }, 500, "bounce");
33320                     this.label[1].attr({ "font-weight": 400 });
33321                 }
33322             };
33323
33324         switch(graphtype){
33325             case 'bar':
33326                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33327                 break;
33328             case 'hbar':
33329                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33330                 break;
33331             case 'pie':
33332 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
33333 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
33334 //            
33335                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
33336                 
33337                 break;
33338
33339         }
33340         
33341         if(this.title){
33342             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
33343         }
33344         
33345     },
33346     
33347     setTitle: function(o)
33348     {
33349         this.title = o;
33350     },
33351     
33352     initEvents: function() {
33353         
33354         if(!this.href){
33355             this.el.on('click', this.onClick, this);
33356         }
33357     },
33358     
33359     onClick : function(e)
33360     {
33361         Roo.log('img onclick');
33362         this.fireEvent('click', this, e);
33363     }
33364    
33365 });
33366
33367  
33368 Roo.bootstrap.dash = {};/*
33369  * - LGPL
33370  *
33371  * numberBox
33372  * 
33373  */
33374 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33375
33376 /**
33377  * @class Roo.bootstrap.dash.NumberBox
33378  * @extends Roo.bootstrap.Component
33379  * Bootstrap NumberBox class
33380  * @cfg {String} headline Box headline
33381  * @cfg {String} content Box content
33382  * @cfg {String} icon Box icon
33383  * @cfg {String} footer Footer text
33384  * @cfg {String} fhref Footer href
33385  * 
33386  * @constructor
33387  * Create a new NumberBox
33388  * @param {Object} config The config object
33389  */
33390
33391
33392 Roo.bootstrap.dash.NumberBox = function(config){
33393     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
33394     
33395 };
33396
33397 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
33398     
33399     headline : '',
33400     content : '',
33401     icon : '',
33402     footer : '',
33403     fhref : '',
33404     ficon : '',
33405     
33406     getAutoCreate : function(){
33407         
33408         var cfg = {
33409             tag : 'div',
33410             cls : 'small-box ',
33411             cn : [
33412                 {
33413                     tag : 'div',
33414                     cls : 'inner',
33415                     cn :[
33416                         {
33417                             tag : 'h3',
33418                             cls : 'roo-headline',
33419                             html : this.headline
33420                         },
33421                         {
33422                             tag : 'p',
33423                             cls : 'roo-content',
33424                             html : this.content
33425                         }
33426                     ]
33427                 }
33428             ]
33429         };
33430         
33431         if(this.icon){
33432             cfg.cn.push({
33433                 tag : 'div',
33434                 cls : 'icon',
33435                 cn :[
33436                     {
33437                         tag : 'i',
33438                         cls : 'ion ' + this.icon
33439                     }
33440                 ]
33441             });
33442         }
33443         
33444         if(this.footer){
33445             var footer = {
33446                 tag : 'a',
33447                 cls : 'small-box-footer',
33448                 href : this.fhref || '#',
33449                 html : this.footer
33450             };
33451             
33452             cfg.cn.push(footer);
33453             
33454         }
33455         
33456         return  cfg;
33457     },
33458
33459     onRender : function(ct,position){
33460         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
33461
33462
33463        
33464                 
33465     },
33466
33467     setHeadline: function (value)
33468     {
33469         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
33470     },
33471     
33472     setFooter: function (value, href)
33473     {
33474         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
33475         
33476         if(href){
33477             this.el.select('a.small-box-footer',true).first().attr('href', href);
33478         }
33479         
33480     },
33481
33482     setContent: function (value)
33483     {
33484         this.el.select('.roo-content',true).first().dom.innerHTML = value;
33485     },
33486
33487     initEvents: function() 
33488     {   
33489         
33490     }
33491     
33492 });
33493
33494  
33495 /*
33496  * - LGPL
33497  *
33498  * TabBox
33499  * 
33500  */
33501 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33502
33503 /**
33504  * @class Roo.bootstrap.dash.TabBox
33505  * @extends Roo.bootstrap.Component
33506  * @children Roo.bootstrap.dash.TabPane
33507  * Bootstrap TabBox class
33508  * @cfg {String} title Title of the TabBox
33509  * @cfg {String} icon Icon of the TabBox
33510  * @cfg {Boolean} showtabs (true|false) show the tabs default true
33511  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
33512  * 
33513  * @constructor
33514  * Create a new TabBox
33515  * @param {Object} config The config object
33516  */
33517
33518
33519 Roo.bootstrap.dash.TabBox = function(config){
33520     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
33521     this.addEvents({
33522         // raw events
33523         /**
33524          * @event addpane
33525          * When a pane is added
33526          * @param {Roo.bootstrap.dash.TabPane} pane
33527          */
33528         "addpane" : true,
33529         /**
33530          * @event activatepane
33531          * When a pane is activated
33532          * @param {Roo.bootstrap.dash.TabPane} pane
33533          */
33534         "activatepane" : true
33535         
33536          
33537     });
33538     
33539     this.panes = [];
33540 };
33541
33542 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
33543
33544     title : '',
33545     icon : false,
33546     showtabs : true,
33547     tabScrollable : false,
33548     
33549     getChildContainer : function()
33550     {
33551         return this.el.select('.tab-content', true).first();
33552     },
33553     
33554     getAutoCreate : function(){
33555         
33556         var header = {
33557             tag: 'li',
33558             cls: 'pull-left header',
33559             html: this.title,
33560             cn : []
33561         };
33562         
33563         if(this.icon){
33564             header.cn.push({
33565                 tag: 'i',
33566                 cls: 'fa ' + this.icon
33567             });
33568         }
33569         
33570         var h = {
33571             tag: 'ul',
33572             cls: 'nav nav-tabs pull-right',
33573             cn: [
33574                 header
33575             ]
33576         };
33577         
33578         if(this.tabScrollable){
33579             h = {
33580                 tag: 'div',
33581                 cls: 'tab-header',
33582                 cn: [
33583                     {
33584                         tag: 'ul',
33585                         cls: 'nav nav-tabs pull-right',
33586                         cn: [
33587                             header
33588                         ]
33589                     }
33590                 ]
33591             };
33592         }
33593         
33594         var cfg = {
33595             tag: 'div',
33596             cls: 'nav-tabs-custom',
33597             cn: [
33598                 h,
33599                 {
33600                     tag: 'div',
33601                     cls: 'tab-content no-padding',
33602                     cn: []
33603                 }
33604             ]
33605         };
33606
33607         return  cfg;
33608     },
33609     initEvents : function()
33610     {
33611         //Roo.log('add add pane handler');
33612         this.on('addpane', this.onAddPane, this);
33613     },
33614      /**
33615      * Updates the box title
33616      * @param {String} html to set the title to.
33617      */
33618     setTitle : function(value)
33619     {
33620         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
33621     },
33622     onAddPane : function(pane)
33623     {
33624         this.panes.push(pane);
33625         //Roo.log('addpane');
33626         //Roo.log(pane);
33627         // tabs are rendere left to right..
33628         if(!this.showtabs){
33629             return;
33630         }
33631         
33632         var ctr = this.el.select('.nav-tabs', true).first();
33633          
33634          
33635         var existing = ctr.select('.nav-tab',true);
33636         var qty = existing.getCount();;
33637         
33638         
33639         var tab = ctr.createChild({
33640             tag : 'li',
33641             cls : 'nav-tab' + (qty ? '' : ' active'),
33642             cn : [
33643                 {
33644                     tag : 'a',
33645                     href:'#',
33646                     html : pane.title
33647                 }
33648             ]
33649         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
33650         pane.tab = tab;
33651         
33652         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
33653         if (!qty) {
33654             pane.el.addClass('active');
33655         }
33656         
33657                 
33658     },
33659     onTabClick : function(ev,un,ob,pane)
33660     {
33661         //Roo.log('tab - prev default');
33662         ev.preventDefault();
33663         
33664         
33665         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
33666         pane.tab.addClass('active');
33667         //Roo.log(pane.title);
33668         this.getChildContainer().select('.tab-pane',true).removeClass('active');
33669         // technically we should have a deactivate event.. but maybe add later.
33670         // and it should not de-activate the selected tab...
33671         this.fireEvent('activatepane', pane);
33672         pane.el.addClass('active');
33673         pane.fireEvent('activate');
33674         
33675         
33676     },
33677     
33678     getActivePane : function()
33679     {
33680         var r = false;
33681         Roo.each(this.panes, function(p) {
33682             if(p.el.hasClass('active')){
33683                 r = p;
33684                 return false;
33685             }
33686             
33687             return;
33688         });
33689         
33690         return r;
33691     }
33692     
33693     
33694 });
33695
33696  
33697 /*
33698  * - LGPL
33699  *
33700  * Tab pane
33701  * 
33702  */
33703 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33704 /**
33705  * @class Roo.bootstrap.TabPane
33706  * @extends Roo.bootstrap.Component
33707  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
33708  * Bootstrap TabPane class
33709  * @cfg {Boolean} active (false | true) Default false
33710  * @cfg {String} title title of panel
33711
33712  * 
33713  * @constructor
33714  * Create a new TabPane
33715  * @param {Object} config The config object
33716  */
33717
33718 Roo.bootstrap.dash.TabPane = function(config){
33719     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
33720     
33721     this.addEvents({
33722         // raw events
33723         /**
33724          * @event activate
33725          * When a pane is activated
33726          * @param {Roo.bootstrap.dash.TabPane} pane
33727          */
33728         "activate" : true
33729          
33730     });
33731 };
33732
33733 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
33734     
33735     active : false,
33736     title : '',
33737     
33738     // the tabBox that this is attached to.
33739     tab : false,
33740      
33741     getAutoCreate : function() 
33742     {
33743         var cfg = {
33744             tag: 'div',
33745             cls: 'tab-pane'
33746         };
33747         
33748         if(this.active){
33749             cfg.cls += ' active';
33750         }
33751         
33752         return cfg;
33753     },
33754     initEvents  : function()
33755     {
33756         //Roo.log('trigger add pane handler');
33757         this.parent().fireEvent('addpane', this)
33758     },
33759     
33760      /**
33761      * Updates the tab title 
33762      * @param {String} html to set the title to.
33763      */
33764     setTitle: function(str)
33765     {
33766         if (!this.tab) {
33767             return;
33768         }
33769         this.title = str;
33770         this.tab.select('a', true).first().dom.innerHTML = str;
33771         
33772     }
33773     
33774     
33775     
33776 });
33777
33778  
33779
33780
33781  /*
33782  * - LGPL
33783  *
33784  * Tooltip
33785  * 
33786  */
33787
33788 /**
33789  * @class Roo.bootstrap.Tooltip
33790  * Bootstrap Tooltip class
33791  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
33792  * to determine which dom element triggers the tooltip.
33793  * 
33794  * It needs to add support for additional attributes like tooltip-position
33795  * 
33796  * @constructor
33797  * Create a new Toolti
33798  * @param {Object} config The config object
33799  */
33800
33801 Roo.bootstrap.Tooltip = function(config){
33802     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
33803     
33804     this.alignment = Roo.bootstrap.Tooltip.alignment;
33805     
33806     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
33807         this.alignment = config.alignment;
33808     }
33809     
33810 };
33811
33812 Roo.apply(Roo.bootstrap.Tooltip, {
33813     /**
33814      * @function init initialize tooltip monitoring.
33815      * @static
33816      */
33817     currentEl : false,
33818     currentTip : false,
33819     currentRegion : false,
33820     
33821     //  init : delay?
33822     
33823     init : function()
33824     {
33825         Roo.get(document).on('mouseover', this.enter ,this);
33826         Roo.get(document).on('mouseout', this.leave, this);
33827          
33828         
33829         this.currentTip = new Roo.bootstrap.Tooltip();
33830     },
33831     
33832     enter : function(ev)
33833     {
33834         var dom = ev.getTarget();
33835         
33836         //Roo.log(['enter',dom]);
33837         var el = Roo.fly(dom);
33838         if (this.currentEl) {
33839             //Roo.log(dom);
33840             //Roo.log(this.currentEl);
33841             //Roo.log(this.currentEl.contains(dom));
33842             if (this.currentEl == el) {
33843                 return;
33844             }
33845             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
33846                 return;
33847             }
33848
33849         }
33850         
33851         if (this.currentTip.el) {
33852             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
33853         }    
33854         //Roo.log(ev);
33855         
33856         if(!el || el.dom == document){
33857             return;
33858         }
33859         
33860         var bindEl = el; 
33861         var pel = false;
33862         if (!el.attr('tooltip')) {
33863             pel = el.findParent("[tooltip]");
33864             if (pel) {
33865                 bindEl = Roo.get(pel);
33866             }
33867         }
33868         
33869        
33870         
33871         // you can not look for children, as if el is the body.. then everythign is the child..
33872         if (!pel && !el.attr('tooltip')) { //
33873             if (!el.select("[tooltip]").elements.length) {
33874                 return;
33875             }
33876             // is the mouse over this child...?
33877             bindEl = el.select("[tooltip]").first();
33878             var xy = ev.getXY();
33879             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
33880                 //Roo.log("not in region.");
33881                 return;
33882             }
33883             //Roo.log("child element over..");
33884             
33885         }
33886         this.currentEl = el;
33887         this.currentTip.bind(bindEl);
33888         this.currentRegion = Roo.lib.Region.getRegion(dom);
33889         this.currentTip.enter();
33890         
33891     },
33892     leave : function(ev)
33893     {
33894         var dom = ev.getTarget();
33895         //Roo.log(['leave',dom]);
33896         if (!this.currentEl) {
33897             return;
33898         }
33899         
33900         
33901         if (dom != this.currentEl.dom) {
33902             return;
33903         }
33904         var xy = ev.getXY();
33905         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
33906             return;
33907         }
33908         // only activate leave if mouse cursor is outside... bounding box..
33909         
33910         
33911         
33912         
33913         if (this.currentTip) {
33914             this.currentTip.leave();
33915         }
33916         //Roo.log('clear currentEl');
33917         this.currentEl = false;
33918         
33919         
33920     },
33921     alignment : {
33922         'left' : ['r-l', [-2,0], 'right'],
33923         'right' : ['l-r', [2,0], 'left'],
33924         'bottom' : ['t-b', [0,2], 'top'],
33925         'top' : [ 'b-t', [0,-2], 'bottom']
33926     }
33927     
33928 });
33929
33930
33931 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
33932     
33933     
33934     bindEl : false,
33935     
33936     delay : null, // can be { show : 300 , hide: 500}
33937     
33938     timeout : null,
33939     
33940     hoverState : null, //???
33941     
33942     placement : 'bottom', 
33943     
33944     alignment : false,
33945     
33946     getAutoCreate : function(){
33947     
33948         var cfg = {
33949            cls : 'tooltip',   
33950            role : 'tooltip',
33951            cn : [
33952                 {
33953                     cls : 'tooltip-arrow arrow'
33954                 },
33955                 {
33956                     cls : 'tooltip-inner'
33957                 }
33958            ]
33959         };
33960         
33961         return cfg;
33962     },
33963     bind : function(el)
33964     {
33965         this.bindEl = el;
33966     },
33967     
33968     initEvents : function()
33969     {
33970         this.arrowEl = this.el.select('.arrow', true).first();
33971         this.innerEl = this.el.select('.tooltip-inner', true).first();
33972     },
33973     
33974     enter : function () {
33975        
33976         if (this.timeout != null) {
33977             clearTimeout(this.timeout);
33978         }
33979         
33980         this.hoverState = 'in';
33981          //Roo.log("enter - show");
33982         if (!this.delay || !this.delay.show) {
33983             this.show();
33984             return;
33985         }
33986         var _t = this;
33987         this.timeout = setTimeout(function () {
33988             if (_t.hoverState == 'in') {
33989                 _t.show();
33990             }
33991         }, this.delay.show);
33992     },
33993     leave : function()
33994     {
33995         clearTimeout(this.timeout);
33996     
33997         this.hoverState = 'out';
33998          if (!this.delay || !this.delay.hide) {
33999             this.hide();
34000             return;
34001         }
34002        
34003         var _t = this;
34004         this.timeout = setTimeout(function () {
34005             //Roo.log("leave - timeout");
34006             
34007             if (_t.hoverState == 'out') {
34008                 _t.hide();
34009                 Roo.bootstrap.Tooltip.currentEl = false;
34010             }
34011         }, delay);
34012     },
34013     
34014     show : function (msg)
34015     {
34016         if (!this.el) {
34017             this.render(document.body);
34018         }
34019         // set content.
34020         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34021         
34022         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34023         
34024         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34025         
34026         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34027                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34028
34029         if(this.bindEl.attr('tooltip-class')) {
34030             this.el.addClass(this.bindEl.attr('tooltip-class'));
34031         }
34032         
34033         var placement = typeof this.placement == 'function' ?
34034             this.placement.call(this, this.el, on_el) :
34035             this.placement;
34036         
34037         if(this.bindEl.attr('tooltip-placement')) {
34038             placement = this.bindEl.attr('tooltip-placement');
34039         }
34040             
34041         var autoToken = /\s?auto?\s?/i;
34042         var autoPlace = autoToken.test(placement);
34043         if (autoPlace) {
34044             placement = placement.replace(autoToken, '') || 'top';
34045         }
34046         
34047         //this.el.detach()
34048         //this.el.setXY([0,0]);
34049         this.el.show();
34050         //this.el.dom.style.display='block';
34051         
34052         //this.el.appendTo(on_el);
34053         
34054         var p = this.getPosition();
34055         var box = this.el.getBox();
34056         
34057         if (autoPlace) {
34058             // fixme..
34059         }
34060         
34061         var align = this.alignment[placement];
34062         
34063         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34064         
34065         if(placement == 'top' || placement == 'bottom'){
34066             if(xy[0] < 0){
34067                 placement = 'right';
34068             }
34069             
34070             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34071                 placement = 'left';
34072             }
34073             
34074             var scroll = Roo.select('body', true).first().getScroll();
34075             
34076             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34077                 placement = 'top';
34078             }
34079             
34080             align = this.alignment[placement];
34081             
34082             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34083             
34084         }
34085         
34086         var elems = document.getElementsByTagName('div');
34087         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34088         for (var i = 0; i < elems.length; i++) {
34089           var zindex = Number.parseInt(
34090                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34091                 10
34092           );
34093           if (zindex > highest) {
34094             highest = zindex;
34095           }
34096         }
34097         
34098         
34099         
34100         this.el.dom.style.zIndex = highest;
34101         
34102         this.el.alignTo(this.bindEl, align[0],align[1]);
34103         //var arrow = this.el.select('.arrow',true).first();
34104         //arrow.set(align[2], 
34105         
34106         this.el.addClass(placement);
34107         this.el.addClass("bs-tooltip-"+ placement);
34108         
34109         this.el.addClass('in fade show');
34110         
34111         this.hoverState = null;
34112         
34113         if (this.el.hasClass('fade')) {
34114             // fade it?
34115         }
34116         
34117         
34118         
34119         
34120         
34121     },
34122     hide : function()
34123     {
34124          
34125         if (!this.el) {
34126             return;
34127         }
34128         //this.el.setXY([0,0]);
34129         if(this.bindEl.attr('tooltip-class')) {
34130             this.el.removeClass(this.bindEl.attr('tooltip-class'));
34131         }
34132         this.el.removeClass(['show', 'in']);
34133         //this.el.hide();
34134         
34135     }
34136     
34137 });
34138  
34139
34140  /*
34141  * - LGPL
34142  *
34143  * Location Picker
34144  * 
34145  */
34146
34147 /**
34148  * @class Roo.bootstrap.LocationPicker
34149  * @extends Roo.bootstrap.Component
34150  * Bootstrap LocationPicker class
34151  * @cfg {Number} latitude Position when init default 0
34152  * @cfg {Number} longitude Position when init default 0
34153  * @cfg {Number} zoom default 15
34154  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
34155  * @cfg {Boolean} mapTypeControl default false
34156  * @cfg {Boolean} disableDoubleClickZoom default false
34157  * @cfg {Boolean} scrollwheel default true
34158  * @cfg {Boolean} streetViewControl default false
34159  * @cfg {Number} radius default 0
34160  * @cfg {String} locationName
34161  * @cfg {Boolean} draggable default true
34162  * @cfg {Boolean} enableAutocomplete default false
34163  * @cfg {Boolean} enableReverseGeocode default true
34164  * @cfg {String} markerTitle
34165  * 
34166  * @constructor
34167  * Create a new LocationPicker
34168  * @param {Object} config The config object
34169  */
34170
34171
34172 Roo.bootstrap.LocationPicker = function(config){
34173     
34174     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
34175     
34176     this.addEvents({
34177         /**
34178          * @event initial
34179          * Fires when the picker initialized.
34180          * @param {Roo.bootstrap.LocationPicker} this
34181          * @param {Google Location} location
34182          */
34183         initial : true,
34184         /**
34185          * @event positionchanged
34186          * Fires when the picker position changed.
34187          * @param {Roo.bootstrap.LocationPicker} this
34188          * @param {Google Location} location
34189          */
34190         positionchanged : true,
34191         /**
34192          * @event resize
34193          * Fires when the map resize.
34194          * @param {Roo.bootstrap.LocationPicker} this
34195          */
34196         resize : true,
34197         /**
34198          * @event show
34199          * Fires when the map show.
34200          * @param {Roo.bootstrap.LocationPicker} this
34201          */
34202         show : true,
34203         /**
34204          * @event hide
34205          * Fires when the map hide.
34206          * @param {Roo.bootstrap.LocationPicker} this
34207          */
34208         hide : true,
34209         /**
34210          * @event mapClick
34211          * Fires when click the map.
34212          * @param {Roo.bootstrap.LocationPicker} this
34213          * @param {Map event} e
34214          */
34215         mapClick : true,
34216         /**
34217          * @event mapRightClick
34218          * Fires when right click the map.
34219          * @param {Roo.bootstrap.LocationPicker} this
34220          * @param {Map event} e
34221          */
34222         mapRightClick : true,
34223         /**
34224          * @event markerClick
34225          * Fires when click the marker.
34226          * @param {Roo.bootstrap.LocationPicker} this
34227          * @param {Map event} e
34228          */
34229         markerClick : true,
34230         /**
34231          * @event markerRightClick
34232          * Fires when right click the marker.
34233          * @param {Roo.bootstrap.LocationPicker} this
34234          * @param {Map event} e
34235          */
34236         markerRightClick : true,
34237         /**
34238          * @event OverlayViewDraw
34239          * Fires when OverlayView Draw
34240          * @param {Roo.bootstrap.LocationPicker} this
34241          */
34242         OverlayViewDraw : true,
34243         /**
34244          * @event OverlayViewOnAdd
34245          * Fires when OverlayView Draw
34246          * @param {Roo.bootstrap.LocationPicker} this
34247          */
34248         OverlayViewOnAdd : true,
34249         /**
34250          * @event OverlayViewOnRemove
34251          * Fires when OverlayView Draw
34252          * @param {Roo.bootstrap.LocationPicker} this
34253          */
34254         OverlayViewOnRemove : true,
34255         /**
34256          * @event OverlayViewShow
34257          * Fires when OverlayView Draw
34258          * @param {Roo.bootstrap.LocationPicker} this
34259          * @param {Pixel} cpx
34260          */
34261         OverlayViewShow : true,
34262         /**
34263          * @event OverlayViewHide
34264          * Fires when OverlayView Draw
34265          * @param {Roo.bootstrap.LocationPicker} this
34266          */
34267         OverlayViewHide : true,
34268         /**
34269          * @event loadexception
34270          * Fires when load google lib failed.
34271          * @param {Roo.bootstrap.LocationPicker} this
34272          */
34273         loadexception : true
34274     });
34275         
34276 };
34277
34278 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
34279     
34280     gMapContext: false,
34281     
34282     latitude: 0,
34283     longitude: 0,
34284     zoom: 15,
34285     mapTypeId: false,
34286     mapTypeControl: false,
34287     disableDoubleClickZoom: false,
34288     scrollwheel: true,
34289     streetViewControl: false,
34290     radius: 0,
34291     locationName: '',
34292     draggable: true,
34293     enableAutocomplete: false,
34294     enableReverseGeocode: true,
34295     markerTitle: '',
34296     
34297     getAutoCreate: function()
34298     {
34299
34300         var cfg = {
34301             tag: 'div',
34302             cls: 'roo-location-picker'
34303         };
34304         
34305         return cfg
34306     },
34307     
34308     initEvents: function(ct, position)
34309     {       
34310         if(!this.el.getWidth() || this.isApplied()){
34311             return;
34312         }
34313         
34314         this.el.setVisibilityMode(Roo.Element.DISPLAY);
34315         
34316         this.initial();
34317     },
34318     
34319     initial: function()
34320     {
34321         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
34322             this.fireEvent('loadexception', this);
34323             return;
34324         }
34325         
34326         if(!this.mapTypeId){
34327             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
34328         }
34329         
34330         this.gMapContext = this.GMapContext();
34331         
34332         this.initOverlayView();
34333         
34334         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
34335         
34336         var _this = this;
34337                 
34338         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
34339             _this.setPosition(_this.gMapContext.marker.position);
34340         });
34341         
34342         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
34343             _this.fireEvent('mapClick', this, event);
34344             
34345         });
34346
34347         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
34348             _this.fireEvent('mapRightClick', this, event);
34349             
34350         });
34351         
34352         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
34353             _this.fireEvent('markerClick', this, event);
34354             
34355         });
34356
34357         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
34358             _this.fireEvent('markerRightClick', this, event);
34359             
34360         });
34361         
34362         this.setPosition(this.gMapContext.location);
34363         
34364         this.fireEvent('initial', this, this.gMapContext.location);
34365     },
34366     
34367     initOverlayView: function()
34368     {
34369         var _this = this;
34370         
34371         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
34372             
34373             draw: function()
34374             {
34375                 _this.fireEvent('OverlayViewDraw', _this);
34376             },
34377             
34378             onAdd: function()
34379             {
34380                 _this.fireEvent('OverlayViewOnAdd', _this);
34381             },
34382             
34383             onRemove: function()
34384             {
34385                 _this.fireEvent('OverlayViewOnRemove', _this);
34386             },
34387             
34388             show: function(cpx)
34389             {
34390                 _this.fireEvent('OverlayViewShow', _this, cpx);
34391             },
34392             
34393             hide: function()
34394             {
34395                 _this.fireEvent('OverlayViewHide', _this);
34396             }
34397             
34398         });
34399     },
34400     
34401     fromLatLngToContainerPixel: function(event)
34402     {
34403         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
34404     },
34405     
34406     isApplied: function() 
34407     {
34408         return this.getGmapContext() == false ? false : true;
34409     },
34410     
34411     getGmapContext: function() 
34412     {
34413         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
34414     },
34415     
34416     GMapContext: function() 
34417     {
34418         var position = new google.maps.LatLng(this.latitude, this.longitude);
34419         
34420         var _map = new google.maps.Map(this.el.dom, {
34421             center: position,
34422             zoom: this.zoom,
34423             mapTypeId: this.mapTypeId,
34424             mapTypeControl: this.mapTypeControl,
34425             disableDoubleClickZoom: this.disableDoubleClickZoom,
34426             scrollwheel: this.scrollwheel,
34427             streetViewControl: this.streetViewControl,
34428             locationName: this.locationName,
34429             draggable: this.draggable,
34430             enableAutocomplete: this.enableAutocomplete,
34431             enableReverseGeocode: this.enableReverseGeocode
34432         });
34433         
34434         var _marker = new google.maps.Marker({
34435             position: position,
34436             map: _map,
34437             title: this.markerTitle,
34438             draggable: this.draggable
34439         });
34440         
34441         return {
34442             map: _map,
34443             marker: _marker,
34444             circle: null,
34445             location: position,
34446             radius: this.radius,
34447             locationName: this.locationName,
34448             addressComponents: {
34449                 formatted_address: null,
34450                 addressLine1: null,
34451                 addressLine2: null,
34452                 streetName: null,
34453                 streetNumber: null,
34454                 city: null,
34455                 district: null,
34456                 state: null,
34457                 stateOrProvince: null
34458             },
34459             settings: this,
34460             domContainer: this.el.dom,
34461             geodecoder: new google.maps.Geocoder()
34462         };
34463     },
34464     
34465     drawCircle: function(center, radius, options) 
34466     {
34467         if (this.gMapContext.circle != null) {
34468             this.gMapContext.circle.setMap(null);
34469         }
34470         if (radius > 0) {
34471             radius *= 1;
34472             options = Roo.apply({}, options, {
34473                 strokeColor: "#0000FF",
34474                 strokeOpacity: .35,
34475                 strokeWeight: 2,
34476                 fillColor: "#0000FF",
34477                 fillOpacity: .2
34478             });
34479             
34480             options.map = this.gMapContext.map;
34481             options.radius = radius;
34482             options.center = center;
34483             this.gMapContext.circle = new google.maps.Circle(options);
34484             return this.gMapContext.circle;
34485         }
34486         
34487         return null;
34488     },
34489     
34490     setPosition: function(location) 
34491     {
34492         this.gMapContext.location = location;
34493         this.gMapContext.marker.setPosition(location);
34494         this.gMapContext.map.panTo(location);
34495         this.drawCircle(location, this.gMapContext.radius, {});
34496         
34497         var _this = this;
34498         
34499         if (this.gMapContext.settings.enableReverseGeocode) {
34500             this.gMapContext.geodecoder.geocode({
34501                 latLng: this.gMapContext.location
34502             }, function(results, status) {
34503                 
34504                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
34505                     _this.gMapContext.locationName = results[0].formatted_address;
34506                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
34507                     
34508                     _this.fireEvent('positionchanged', this, location);
34509                 }
34510             });
34511             
34512             return;
34513         }
34514         
34515         this.fireEvent('positionchanged', this, location);
34516     },
34517     
34518     resize: function()
34519     {
34520         google.maps.event.trigger(this.gMapContext.map, "resize");
34521         
34522         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
34523         
34524         this.fireEvent('resize', this);
34525     },
34526     
34527     setPositionByLatLng: function(latitude, longitude)
34528     {
34529         this.setPosition(new google.maps.LatLng(latitude, longitude));
34530     },
34531     
34532     getCurrentPosition: function() 
34533     {
34534         return {
34535             latitude: this.gMapContext.location.lat(),
34536             longitude: this.gMapContext.location.lng()
34537         };
34538     },
34539     
34540     getAddressName: function() 
34541     {
34542         return this.gMapContext.locationName;
34543     },
34544     
34545     getAddressComponents: function() 
34546     {
34547         return this.gMapContext.addressComponents;
34548     },
34549     
34550     address_component_from_google_geocode: function(address_components) 
34551     {
34552         var result = {};
34553         
34554         for (var i = 0; i < address_components.length; i++) {
34555             var component = address_components[i];
34556             if (component.types.indexOf("postal_code") >= 0) {
34557                 result.postalCode = component.short_name;
34558             } else if (component.types.indexOf("street_number") >= 0) {
34559                 result.streetNumber = component.short_name;
34560             } else if (component.types.indexOf("route") >= 0) {
34561                 result.streetName = component.short_name;
34562             } else if (component.types.indexOf("neighborhood") >= 0) {
34563                 result.city = component.short_name;
34564             } else if (component.types.indexOf("locality") >= 0) {
34565                 result.city = component.short_name;
34566             } else if (component.types.indexOf("sublocality") >= 0) {
34567                 result.district = component.short_name;
34568             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
34569                 result.stateOrProvince = component.short_name;
34570             } else if (component.types.indexOf("country") >= 0) {
34571                 result.country = component.short_name;
34572             }
34573         }
34574         
34575         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
34576         result.addressLine2 = "";
34577         return result;
34578     },
34579     
34580     setZoomLevel: function(zoom)
34581     {
34582         this.gMapContext.map.setZoom(zoom);
34583     },
34584     
34585     show: function()
34586     {
34587         if(!this.el){
34588             return;
34589         }
34590         
34591         this.el.show();
34592         
34593         this.resize();
34594         
34595         this.fireEvent('show', this);
34596     },
34597     
34598     hide: function()
34599     {
34600         if(!this.el){
34601             return;
34602         }
34603         
34604         this.el.hide();
34605         
34606         this.fireEvent('hide', this);
34607     }
34608     
34609 });
34610
34611 Roo.apply(Roo.bootstrap.LocationPicker, {
34612     
34613     OverlayView : function(map, options)
34614     {
34615         options = options || {};
34616         
34617         this.setMap(map);
34618     }
34619     
34620     
34621 });/**
34622  * @class Roo.bootstrap.Alert
34623  * @extends Roo.bootstrap.Component
34624  * Bootstrap Alert class - shows an alert area box
34625  * eg
34626  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
34627   Enter a valid email address
34628 </div>
34629  * @licence LGPL
34630  * @cfg {String} title The title of alert
34631  * @cfg {String} html The content of alert
34632  * @cfg {String} weight (success|info|warning|danger) Weight of the message
34633  * @cfg {String} fa font-awesomeicon
34634  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
34635  * @cfg {Boolean} close true to show a x closer
34636  * 
34637  * 
34638  * @constructor
34639  * Create a new alert
34640  * @param {Object} config The config object
34641  */
34642
34643
34644 Roo.bootstrap.Alert = function(config){
34645     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
34646     
34647 };
34648
34649 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
34650     
34651     title: '',
34652     html: '',
34653     weight: false,
34654     fa: false,
34655     faicon: false, // BC
34656     close : false,
34657     
34658     
34659     getAutoCreate : function()
34660     {
34661         
34662         var cfg = {
34663             tag : 'div',
34664             cls : 'alert',
34665             cn : [
34666                 {
34667                     tag: 'button',
34668                     type :  "button",
34669                     cls: "close",
34670                     html : '×',
34671                     style : this.close ? '' : 'display:none'
34672                 },
34673                 {
34674                     tag : 'i',
34675                     cls : 'roo-alert-icon'
34676                     
34677                 },
34678                 {
34679                     tag : 'b',
34680                     cls : 'roo-alert-title',
34681                     html : this.title
34682                 },
34683                 {
34684                     tag : 'span',
34685                     cls : 'roo-alert-text',
34686                     html : this.html
34687                 }
34688             ]
34689         };
34690         
34691         if(this.faicon){
34692             cfg.cn[0].cls += ' fa ' + this.faicon;
34693         }
34694         if(this.fa){
34695             cfg.cn[0].cls += ' fa ' + this.fa;
34696         }
34697         
34698         if(this.weight){
34699             cfg.cls += ' alert-' + this.weight;
34700         }
34701         
34702         return cfg;
34703     },
34704     
34705     initEvents: function() 
34706     {
34707         this.el.setVisibilityMode(Roo.Element.DISPLAY);
34708         this.titleEl =  this.el.select('.roo-alert-title',true).first();
34709         this.iconEl = this.el.select('.roo-alert-icon',true).first();
34710         this.htmlEl = this.el.select('.roo-alert-text',true).first();
34711         if (this.seconds > 0) {
34712             this.hide.defer(this.seconds, this);
34713         }
34714     },
34715     /**
34716      * Set the Title Message HTML
34717      * @param {String} html
34718      */
34719     setTitle : function(str)
34720     {
34721         this.titleEl.dom.innerHTML = str;
34722     },
34723      
34724      /**
34725      * Set the Body Message HTML
34726      * @param {String} html
34727      */
34728     setHtml : function(str)
34729     {
34730         this.htmlEl.dom.innerHTML = str;
34731     },
34732     /**
34733      * Set the Weight of the alert
34734      * @param {String} (success|info|warning|danger) weight
34735      */
34736     
34737     setWeight : function(weight)
34738     {
34739         if(this.weight){
34740             this.el.removeClass('alert-' + this.weight);
34741         }
34742         
34743         this.weight = weight;
34744         
34745         this.el.addClass('alert-' + this.weight);
34746     },
34747       /**
34748      * Set the Icon of the alert
34749      * @param {String} see fontawsome names (name without the 'fa-' bit)
34750      */
34751     setIcon : function(icon)
34752     {
34753         if(this.faicon){
34754             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
34755         }
34756         
34757         this.faicon = icon;
34758         
34759         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
34760     },
34761     /**
34762      * Hide the Alert
34763      */
34764     hide: function() 
34765     {
34766         this.el.hide();   
34767     },
34768     /**
34769      * Show the Alert
34770      */
34771     show: function() 
34772     {  
34773         this.el.show();   
34774     }
34775     
34776 });
34777
34778  
34779 /*
34780 * Licence: LGPL
34781 */
34782
34783 /**
34784  * @class Roo.bootstrap.UploadCropbox
34785  * @extends Roo.bootstrap.Component
34786  * Bootstrap UploadCropbox class
34787  * @cfg {String} emptyText show when image has been loaded
34788  * @cfg {String} rotateNotify show when image too small to rotate
34789  * @cfg {Number} errorTimeout default 3000
34790  * @cfg {Number} minWidth default 300
34791  * @cfg {Number} minHeight default 300
34792  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
34793  * @cfg {Boolean} isDocument (true|false) default false
34794  * @cfg {String} url action url
34795  * @cfg {String} paramName default 'imageUpload'
34796  * @cfg {String} method default POST
34797  * @cfg {Boolean} loadMask (true|false) default true
34798  * @cfg {Boolean} loadingText default 'Loading...'
34799  * 
34800  * @constructor
34801  * Create a new UploadCropbox
34802  * @param {Object} config The config object
34803  */
34804
34805 Roo.bootstrap.UploadCropbox = function(config){
34806     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
34807     
34808     this.addEvents({
34809         /**
34810          * @event beforeselectfile
34811          * Fire before select file
34812          * @param {Roo.bootstrap.UploadCropbox} this
34813          */
34814         "beforeselectfile" : true,
34815         /**
34816          * @event initial
34817          * Fire after initEvent
34818          * @param {Roo.bootstrap.UploadCropbox} this
34819          */
34820         "initial" : true,
34821         /**
34822          * @event crop
34823          * Fire after initEvent
34824          * @param {Roo.bootstrap.UploadCropbox} this
34825          * @param {String} data
34826          */
34827         "crop" : true,
34828         /**
34829          * @event prepare
34830          * Fire when preparing the file data
34831          * @param {Roo.bootstrap.UploadCropbox} this
34832          * @param {Object} file
34833          */
34834         "prepare" : true,
34835         /**
34836          * @event exception
34837          * Fire when get exception
34838          * @param {Roo.bootstrap.UploadCropbox} this
34839          * @param {XMLHttpRequest} xhr
34840          */
34841         "exception" : true,
34842         /**
34843          * @event beforeloadcanvas
34844          * Fire before load the canvas
34845          * @param {Roo.bootstrap.UploadCropbox} this
34846          * @param {String} src
34847          */
34848         "beforeloadcanvas" : true,
34849         /**
34850          * @event trash
34851          * Fire when trash image
34852          * @param {Roo.bootstrap.UploadCropbox} this
34853          */
34854         "trash" : true,
34855         /**
34856          * @event download
34857          * Fire when download the image
34858          * @param {Roo.bootstrap.UploadCropbox} this
34859          */
34860         "download" : true,
34861         /**
34862          * @event footerbuttonclick
34863          * Fire when footerbuttonclick
34864          * @param {Roo.bootstrap.UploadCropbox} this
34865          * @param {String} type
34866          */
34867         "footerbuttonclick" : true,
34868         /**
34869          * @event resize
34870          * Fire when resize
34871          * @param {Roo.bootstrap.UploadCropbox} this
34872          */
34873         "resize" : true,
34874         /**
34875          * @event rotate
34876          * Fire when rotate the image
34877          * @param {Roo.bootstrap.UploadCropbox} this
34878          * @param {String} pos
34879          */
34880         "rotate" : true,
34881         /**
34882          * @event inspect
34883          * Fire when inspect the file
34884          * @param {Roo.bootstrap.UploadCropbox} this
34885          * @param {Object} file
34886          */
34887         "inspect" : true,
34888         /**
34889          * @event upload
34890          * Fire when xhr upload the file
34891          * @param {Roo.bootstrap.UploadCropbox} this
34892          * @param {Object} data
34893          */
34894         "upload" : true,
34895         /**
34896          * @event arrange
34897          * Fire when arrange the file data
34898          * @param {Roo.bootstrap.UploadCropbox} this
34899          * @param {Object} formData
34900          */
34901         "arrange" : true
34902     });
34903     
34904     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
34905 };
34906
34907 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
34908     
34909     emptyText : 'Click to upload image',
34910     rotateNotify : 'Image is too small to rotate',
34911     errorTimeout : 3000,
34912     scale : 0,
34913     baseScale : 1,
34914     rotate : 0,
34915     dragable : false,
34916     pinching : false,
34917     mouseX : 0,
34918     mouseY : 0,
34919     cropData : false,
34920     minWidth : 300,
34921     minHeight : 300,
34922     file : false,
34923     exif : {},
34924     baseRotate : 1,
34925     cropType : 'image/jpeg',
34926     buttons : false,
34927     canvasLoaded : false,
34928     isDocument : false,
34929     method : 'POST',
34930     paramName : 'imageUpload',
34931     loadMask : true,
34932     loadingText : 'Loading...',
34933     maskEl : false,
34934     
34935     getAutoCreate : function()
34936     {
34937         var cfg = {
34938             tag : 'div',
34939             cls : 'roo-upload-cropbox',
34940             cn : [
34941                 {
34942                     tag : 'input',
34943                     cls : 'roo-upload-cropbox-selector',
34944                     type : 'file'
34945                 },
34946                 {
34947                     tag : 'div',
34948                     cls : 'roo-upload-cropbox-body',
34949                     style : 'cursor:pointer',
34950                     cn : [
34951                         {
34952                             tag : 'div',
34953                             cls : 'roo-upload-cropbox-preview'
34954                         },
34955                         {
34956                             tag : 'div',
34957                             cls : 'roo-upload-cropbox-thumb'
34958                         },
34959                         {
34960                             tag : 'div',
34961                             cls : 'roo-upload-cropbox-empty-notify',
34962                             html : this.emptyText
34963                         },
34964                         {
34965                             tag : 'div',
34966                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
34967                             html : this.rotateNotify
34968                         }
34969                     ]
34970                 },
34971                 {
34972                     tag : 'div',
34973                     cls : 'roo-upload-cropbox-footer',
34974                     cn : {
34975                         tag : 'div',
34976                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
34977                         cn : []
34978                     }
34979                 }
34980             ]
34981         };
34982         
34983         return cfg;
34984     },
34985     
34986     onRender : function(ct, position)
34987     {
34988         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
34989         
34990         if (this.buttons.length) {
34991             
34992             Roo.each(this.buttons, function(bb) {
34993                 
34994                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
34995                 
34996                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
34997                 
34998             }, this);
34999         }
35000         
35001         if(this.loadMask){
35002             this.maskEl = this.el;
35003         }
35004     },
35005     
35006     initEvents : function()
35007     {
35008         this.urlAPI = (window.createObjectURL && window) || 
35009                                 (window.URL && URL.revokeObjectURL && URL) || 
35010                                 (window.webkitURL && webkitURL);
35011                         
35012         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35013         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35014         
35015         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35016         this.selectorEl.hide();
35017         
35018         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35019         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35020         
35021         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35022         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35023         this.thumbEl.hide();
35024         
35025         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35026         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35027         
35028         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35029         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35030         this.errorEl.hide();
35031         
35032         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35033         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35034         this.footerEl.hide();
35035         
35036         this.setThumbBoxSize();
35037         
35038         this.bind();
35039         
35040         this.resize();
35041         
35042         this.fireEvent('initial', this);
35043     },
35044
35045     bind : function()
35046     {
35047         var _this = this;
35048         
35049         window.addEventListener("resize", function() { _this.resize(); } );
35050         
35051         this.bodyEl.on('click', this.beforeSelectFile, this);
35052         
35053         if(Roo.isTouch){
35054             this.bodyEl.on('touchstart', this.onTouchStart, this);
35055             this.bodyEl.on('touchmove', this.onTouchMove, this);
35056             this.bodyEl.on('touchend', this.onTouchEnd, this);
35057         }
35058         
35059         if(!Roo.isTouch){
35060             this.bodyEl.on('mousedown', this.onMouseDown, this);
35061             this.bodyEl.on('mousemove', this.onMouseMove, this);
35062             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35063             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35064             Roo.get(document).on('mouseup', this.onMouseUp, this);
35065         }
35066         
35067         this.selectorEl.on('change', this.onFileSelected, this);
35068     },
35069     
35070     reset : function()
35071     {    
35072         this.scale = 0;
35073         this.baseScale = 1;
35074         this.rotate = 0;
35075         this.baseRotate = 1;
35076         this.dragable = false;
35077         this.pinching = false;
35078         this.mouseX = 0;
35079         this.mouseY = 0;
35080         this.cropData = false;
35081         this.notifyEl.dom.innerHTML = this.emptyText;
35082         
35083         this.selectorEl.dom.value = '';
35084         
35085     },
35086     
35087     resize : function()
35088     {
35089         if(this.fireEvent('resize', this) != false){
35090             this.setThumbBoxPosition();
35091             this.setCanvasPosition();
35092         }
35093     },
35094     
35095     onFooterButtonClick : function(e, el, o, type)
35096     {
35097         switch (type) {
35098             case 'rotate-left' :
35099                 this.onRotateLeft(e);
35100                 break;
35101             case 'rotate-right' :
35102                 this.onRotateRight(e);
35103                 break;
35104             case 'picture' :
35105                 this.beforeSelectFile(e);
35106                 break;
35107             case 'trash' :
35108                 this.trash(e);
35109                 break;
35110             case 'crop' :
35111                 this.crop(e);
35112                 break;
35113             case 'download' :
35114                 this.download(e);
35115                 break;
35116             default :
35117                 break;
35118         }
35119         
35120         this.fireEvent('footerbuttonclick', this, type);
35121     },
35122     
35123     beforeSelectFile : function(e)
35124     {
35125         e.preventDefault();
35126         
35127         if(this.fireEvent('beforeselectfile', this) != false){
35128             this.selectorEl.dom.click();
35129         }
35130     },
35131     
35132     onFileSelected : function(e)
35133     {
35134         e.preventDefault();
35135         
35136         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35137             return;
35138         }
35139         
35140         var file = this.selectorEl.dom.files[0];
35141         
35142         if(this.fireEvent('inspect', this, file) != false){
35143             this.prepare(file);
35144         }
35145         
35146     },
35147     
35148     trash : function(e)
35149     {
35150         this.fireEvent('trash', this);
35151     },
35152     
35153     download : function(e)
35154     {
35155         this.fireEvent('download', this);
35156     },
35157     
35158     loadCanvas : function(src)
35159     {   
35160         if(this.fireEvent('beforeloadcanvas', this, src) != false){
35161             
35162             this.reset();
35163             
35164             this.imageEl = document.createElement('img');
35165             
35166             var _this = this;
35167             
35168             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
35169             
35170             this.imageEl.src = src;
35171         }
35172     },
35173     
35174     onLoadCanvas : function()
35175     {   
35176         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
35177         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
35178         
35179         this.bodyEl.un('click', this.beforeSelectFile, this);
35180         
35181         this.notifyEl.hide();
35182         this.thumbEl.show();
35183         this.footerEl.show();
35184         
35185         this.baseRotateLevel();
35186         
35187         if(this.isDocument){
35188             this.setThumbBoxSize();
35189         }
35190         
35191         this.setThumbBoxPosition();
35192         
35193         this.baseScaleLevel();
35194         
35195         this.draw();
35196         
35197         this.resize();
35198         
35199         this.canvasLoaded = true;
35200         
35201         if(this.loadMask){
35202             this.maskEl.unmask();
35203         }
35204         
35205     },
35206     
35207     setCanvasPosition : function()
35208     {   
35209         if(!this.canvasEl){
35210             return;
35211         }
35212         
35213         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
35214         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
35215         
35216         this.previewEl.setLeft(pw);
35217         this.previewEl.setTop(ph);
35218         
35219     },
35220     
35221     onMouseDown : function(e)
35222     {   
35223         e.stopEvent();
35224         
35225         this.dragable = true;
35226         this.pinching = false;
35227         
35228         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
35229             this.dragable = false;
35230             return;
35231         }
35232         
35233         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35234         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35235         
35236     },
35237     
35238     onMouseMove : function(e)
35239     {   
35240         e.stopEvent();
35241         
35242         if(!this.canvasLoaded){
35243             return;
35244         }
35245         
35246         if (!this.dragable){
35247             return;
35248         }
35249         
35250         var minX = Math.ceil(this.thumbEl.getLeft(true));
35251         var minY = Math.ceil(this.thumbEl.getTop(true));
35252         
35253         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
35254         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
35255         
35256         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35257         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35258         
35259         x = x - this.mouseX;
35260         y = y - this.mouseY;
35261         
35262         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
35263         var bgY = Math.ceil(y + this.previewEl.getTop(true));
35264         
35265         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
35266         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
35267         
35268         this.previewEl.setLeft(bgX);
35269         this.previewEl.setTop(bgY);
35270         
35271         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35272         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35273     },
35274     
35275     onMouseUp : function(e)
35276     {   
35277         e.stopEvent();
35278         
35279         this.dragable = false;
35280     },
35281     
35282     onMouseWheel : function(e)
35283     {   
35284         e.stopEvent();
35285         
35286         this.startScale = this.scale;
35287         
35288         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
35289         
35290         if(!this.zoomable()){
35291             this.scale = this.startScale;
35292             return;
35293         }
35294         
35295         this.draw();
35296         
35297         return;
35298     },
35299     
35300     zoomable : function()
35301     {
35302         var minScale = this.thumbEl.getWidth() / this.minWidth;
35303         
35304         if(this.minWidth < this.minHeight){
35305             minScale = this.thumbEl.getHeight() / this.minHeight;
35306         }
35307         
35308         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
35309         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
35310         
35311         if(
35312                 this.isDocument &&
35313                 (this.rotate == 0 || this.rotate == 180) && 
35314                 (
35315                     width > this.imageEl.OriginWidth || 
35316                     height > this.imageEl.OriginHeight ||
35317                     (width < this.minWidth && height < this.minHeight)
35318                 )
35319         ){
35320             return false;
35321         }
35322         
35323         if(
35324                 this.isDocument &&
35325                 (this.rotate == 90 || this.rotate == 270) && 
35326                 (
35327                     width > this.imageEl.OriginWidth || 
35328                     height > this.imageEl.OriginHeight ||
35329                     (width < this.minHeight && height < this.minWidth)
35330                 )
35331         ){
35332             return false;
35333         }
35334         
35335         if(
35336                 !this.isDocument &&
35337                 (this.rotate == 0 || this.rotate == 180) && 
35338                 (
35339                     width < this.minWidth || 
35340                     width > this.imageEl.OriginWidth || 
35341                     height < this.minHeight || 
35342                     height > this.imageEl.OriginHeight
35343                 )
35344         ){
35345             return false;
35346         }
35347         
35348         if(
35349                 !this.isDocument &&
35350                 (this.rotate == 90 || this.rotate == 270) && 
35351                 (
35352                     width < this.minHeight || 
35353                     width > this.imageEl.OriginWidth || 
35354                     height < this.minWidth || 
35355                     height > this.imageEl.OriginHeight
35356                 )
35357         ){
35358             return false;
35359         }
35360         
35361         return true;
35362         
35363     },
35364     
35365     onRotateLeft : function(e)
35366     {   
35367         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35368             
35369             var minScale = this.thumbEl.getWidth() / this.minWidth;
35370             
35371             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35372             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35373             
35374             this.startScale = this.scale;
35375             
35376             while (this.getScaleLevel() < minScale){
35377             
35378                 this.scale = this.scale + 1;
35379                 
35380                 if(!this.zoomable()){
35381                     break;
35382                 }
35383                 
35384                 if(
35385                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35386                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35387                 ){
35388                     continue;
35389                 }
35390                 
35391                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35392
35393                 this.draw();
35394                 
35395                 return;
35396             }
35397             
35398             this.scale = this.startScale;
35399             
35400             this.onRotateFail();
35401             
35402             return false;
35403         }
35404         
35405         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35406
35407         if(this.isDocument){
35408             this.setThumbBoxSize();
35409             this.setThumbBoxPosition();
35410             this.setCanvasPosition();
35411         }
35412         
35413         this.draw();
35414         
35415         this.fireEvent('rotate', this, 'left');
35416         
35417     },
35418     
35419     onRotateRight : function(e)
35420     {
35421         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35422             
35423             var minScale = this.thumbEl.getWidth() / this.minWidth;
35424         
35425             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35426             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35427             
35428             this.startScale = this.scale;
35429             
35430             while (this.getScaleLevel() < minScale){
35431             
35432                 this.scale = this.scale + 1;
35433                 
35434                 if(!this.zoomable()){
35435                     break;
35436                 }
35437                 
35438                 if(
35439                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35440                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35441                 ){
35442                     continue;
35443                 }
35444                 
35445                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35446
35447                 this.draw();
35448                 
35449                 return;
35450             }
35451             
35452             this.scale = this.startScale;
35453             
35454             this.onRotateFail();
35455             
35456             return false;
35457         }
35458         
35459         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35460
35461         if(this.isDocument){
35462             this.setThumbBoxSize();
35463             this.setThumbBoxPosition();
35464             this.setCanvasPosition();
35465         }
35466         
35467         this.draw();
35468         
35469         this.fireEvent('rotate', this, 'right');
35470     },
35471     
35472     onRotateFail : function()
35473     {
35474         this.errorEl.show(true);
35475         
35476         var _this = this;
35477         
35478         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
35479     },
35480     
35481     draw : function()
35482     {
35483         this.previewEl.dom.innerHTML = '';
35484         
35485         var canvasEl = document.createElement("canvas");
35486         
35487         var contextEl = canvasEl.getContext("2d");
35488         
35489         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35490         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35491         var center = this.imageEl.OriginWidth / 2;
35492         
35493         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
35494             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35495             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35496             center = this.imageEl.OriginHeight / 2;
35497         }
35498         
35499         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
35500         
35501         contextEl.translate(center, center);
35502         contextEl.rotate(this.rotate * Math.PI / 180);
35503
35504         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35505         
35506         this.canvasEl = document.createElement("canvas");
35507         
35508         this.contextEl = this.canvasEl.getContext("2d");
35509         
35510         switch (this.rotate) {
35511             case 0 :
35512                 
35513                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35514                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35515                 
35516                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35517                 
35518                 break;
35519             case 90 : 
35520                 
35521                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35522                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35523                 
35524                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35525                     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);
35526                     break;
35527                 }
35528                 
35529                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35530                 
35531                 break;
35532             case 180 :
35533                 
35534                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35535                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35536                 
35537                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35538                     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);
35539                     break;
35540                 }
35541                 
35542                 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);
35543                 
35544                 break;
35545             case 270 :
35546                 
35547                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35548                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35549         
35550                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35551                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35552                     break;
35553                 }
35554                 
35555                 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);
35556                 
35557                 break;
35558             default : 
35559                 break;
35560         }
35561         
35562         this.previewEl.appendChild(this.canvasEl);
35563         
35564         this.setCanvasPosition();
35565     },
35566     
35567     crop : function()
35568     {
35569         if(!this.canvasLoaded){
35570             return;
35571         }
35572         
35573         var imageCanvas = document.createElement("canvas");
35574         
35575         var imageContext = imageCanvas.getContext("2d");
35576         
35577         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
35578         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
35579         
35580         var center = imageCanvas.width / 2;
35581         
35582         imageContext.translate(center, center);
35583         
35584         imageContext.rotate(this.rotate * Math.PI / 180);
35585         
35586         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35587         
35588         var canvas = document.createElement("canvas");
35589         
35590         var context = canvas.getContext("2d");
35591                 
35592         canvas.width = this.minWidth;
35593         canvas.height = this.minHeight;
35594
35595         switch (this.rotate) {
35596             case 0 :
35597                 
35598                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
35599                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
35600                 
35601                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35602                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35603                 
35604                 var targetWidth = this.minWidth - 2 * x;
35605                 var targetHeight = this.minHeight - 2 * y;
35606                 
35607                 var scale = 1;
35608                 
35609                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35610                     scale = targetWidth / width;
35611                 }
35612                 
35613                 if(x > 0 && y == 0){
35614                     scale = targetHeight / height;
35615                 }
35616                 
35617                 if(x > 0 && y > 0){
35618                     scale = targetWidth / width;
35619                     
35620                     if(width < height){
35621                         scale = targetHeight / height;
35622                     }
35623                 }
35624                 
35625                 context.scale(scale, scale);
35626                 
35627                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35628                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35629
35630                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35631                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35632
35633                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35634                 
35635                 break;
35636             case 90 : 
35637                 
35638                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
35639                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
35640                 
35641                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35642                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35643                 
35644                 var targetWidth = this.minWidth - 2 * x;
35645                 var targetHeight = this.minHeight - 2 * y;
35646                 
35647                 var scale = 1;
35648                 
35649                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35650                     scale = targetWidth / width;
35651                 }
35652                 
35653                 if(x > 0 && y == 0){
35654                     scale = targetHeight / height;
35655                 }
35656                 
35657                 if(x > 0 && y > 0){
35658                     scale = targetWidth / width;
35659                     
35660                     if(width < height){
35661                         scale = targetHeight / height;
35662                     }
35663                 }
35664                 
35665                 context.scale(scale, scale);
35666                 
35667                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35668                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35669
35670                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35671                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35672                 
35673                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
35674                 
35675                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35676                 
35677                 break;
35678             case 180 :
35679                 
35680                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
35681                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
35682                 
35683                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35684                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35685                 
35686                 var targetWidth = this.minWidth - 2 * x;
35687                 var targetHeight = this.minHeight - 2 * y;
35688                 
35689                 var scale = 1;
35690                 
35691                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35692                     scale = targetWidth / width;
35693                 }
35694                 
35695                 if(x > 0 && y == 0){
35696                     scale = targetHeight / height;
35697                 }
35698                 
35699                 if(x > 0 && y > 0){
35700                     scale = targetWidth / width;
35701                     
35702                     if(width < height){
35703                         scale = targetHeight / height;
35704                     }
35705                 }
35706                 
35707                 context.scale(scale, scale);
35708                 
35709                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35710                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35711
35712                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35713                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35714
35715                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35716                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
35717                 
35718                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35719                 
35720                 break;
35721             case 270 :
35722                 
35723                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
35724                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
35725                 
35726                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35727                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35728                 
35729                 var targetWidth = this.minWidth - 2 * x;
35730                 var targetHeight = this.minHeight - 2 * y;
35731                 
35732                 var scale = 1;
35733                 
35734                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35735                     scale = targetWidth / width;
35736                 }
35737                 
35738                 if(x > 0 && y == 0){
35739                     scale = targetHeight / height;
35740                 }
35741                 
35742                 if(x > 0 && y > 0){
35743                     scale = targetWidth / width;
35744                     
35745                     if(width < height){
35746                         scale = targetHeight / height;
35747                     }
35748                 }
35749                 
35750                 context.scale(scale, scale);
35751                 
35752                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35753                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35754
35755                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35756                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35757                 
35758                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35759                 
35760                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35761                 
35762                 break;
35763             default : 
35764                 break;
35765         }
35766         
35767         this.cropData = canvas.toDataURL(this.cropType);
35768         
35769         if(this.fireEvent('crop', this, this.cropData) !== false){
35770             this.process(this.file, this.cropData);
35771         }
35772         
35773         return;
35774         
35775     },
35776     
35777     setThumbBoxSize : function()
35778     {
35779         var width, height;
35780         
35781         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
35782             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
35783             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
35784             
35785             this.minWidth = width;
35786             this.minHeight = height;
35787             
35788             if(this.rotate == 90 || this.rotate == 270){
35789                 this.minWidth = height;
35790                 this.minHeight = width;
35791             }
35792         }
35793         
35794         height = 300;
35795         width = Math.ceil(this.minWidth * height / this.minHeight);
35796         
35797         if(this.minWidth > this.minHeight){
35798             width = 300;
35799             height = Math.ceil(this.minHeight * width / this.minWidth);
35800         }
35801         
35802         this.thumbEl.setStyle({
35803             width : width + 'px',
35804             height : height + 'px'
35805         });
35806
35807         return;
35808             
35809     },
35810     
35811     setThumbBoxPosition : function()
35812     {
35813         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
35814         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
35815         
35816         this.thumbEl.setLeft(x);
35817         this.thumbEl.setTop(y);
35818         
35819     },
35820     
35821     baseRotateLevel : function()
35822     {
35823         this.baseRotate = 1;
35824         
35825         if(
35826                 typeof(this.exif) != 'undefined' &&
35827                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
35828                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
35829         ){
35830             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
35831         }
35832         
35833         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
35834         
35835     },
35836     
35837     baseScaleLevel : function()
35838     {
35839         var width, height;
35840         
35841         if(this.isDocument){
35842             
35843             if(this.baseRotate == 6 || this.baseRotate == 8){
35844             
35845                 height = this.thumbEl.getHeight();
35846                 this.baseScale = height / this.imageEl.OriginWidth;
35847
35848                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
35849                     width = this.thumbEl.getWidth();
35850                     this.baseScale = width / this.imageEl.OriginHeight;
35851                 }
35852
35853                 return;
35854             }
35855
35856             height = this.thumbEl.getHeight();
35857             this.baseScale = height / this.imageEl.OriginHeight;
35858
35859             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
35860                 width = this.thumbEl.getWidth();
35861                 this.baseScale = width / this.imageEl.OriginWidth;
35862             }
35863
35864             return;
35865         }
35866         
35867         if(this.baseRotate == 6 || this.baseRotate == 8){
35868             
35869             width = this.thumbEl.getHeight();
35870             this.baseScale = width / this.imageEl.OriginHeight;
35871             
35872             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
35873                 height = this.thumbEl.getWidth();
35874                 this.baseScale = height / this.imageEl.OriginHeight;
35875             }
35876             
35877             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35878                 height = this.thumbEl.getWidth();
35879                 this.baseScale = height / this.imageEl.OriginHeight;
35880                 
35881                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
35882                     width = this.thumbEl.getHeight();
35883                     this.baseScale = width / this.imageEl.OriginWidth;
35884                 }
35885             }
35886             
35887             return;
35888         }
35889         
35890         width = this.thumbEl.getWidth();
35891         this.baseScale = width / this.imageEl.OriginWidth;
35892         
35893         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
35894             height = this.thumbEl.getHeight();
35895             this.baseScale = height / this.imageEl.OriginHeight;
35896         }
35897         
35898         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35899             
35900             height = this.thumbEl.getHeight();
35901             this.baseScale = height / this.imageEl.OriginHeight;
35902             
35903             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
35904                 width = this.thumbEl.getWidth();
35905                 this.baseScale = width / this.imageEl.OriginWidth;
35906             }
35907             
35908         }
35909         
35910         return;
35911     },
35912     
35913     getScaleLevel : function()
35914     {
35915         return this.baseScale * Math.pow(1.1, this.scale);
35916     },
35917     
35918     onTouchStart : function(e)
35919     {
35920         if(!this.canvasLoaded){
35921             this.beforeSelectFile(e);
35922             return;
35923         }
35924         
35925         var touches = e.browserEvent.touches;
35926         
35927         if(!touches){
35928             return;
35929         }
35930         
35931         if(touches.length == 1){
35932             this.onMouseDown(e);
35933             return;
35934         }
35935         
35936         if(touches.length != 2){
35937             return;
35938         }
35939         
35940         var coords = [];
35941         
35942         for(var i = 0, finger; finger = touches[i]; i++){
35943             coords.push(finger.pageX, finger.pageY);
35944         }
35945         
35946         var x = Math.pow(coords[0] - coords[2], 2);
35947         var y = Math.pow(coords[1] - coords[3], 2);
35948         
35949         this.startDistance = Math.sqrt(x + y);
35950         
35951         this.startScale = this.scale;
35952         
35953         this.pinching = true;
35954         this.dragable = false;
35955         
35956     },
35957     
35958     onTouchMove : function(e)
35959     {
35960         if(!this.pinching && !this.dragable){
35961             return;
35962         }
35963         
35964         var touches = e.browserEvent.touches;
35965         
35966         if(!touches){
35967             return;
35968         }
35969         
35970         if(this.dragable){
35971             this.onMouseMove(e);
35972             return;
35973         }
35974         
35975         var coords = [];
35976         
35977         for(var i = 0, finger; finger = touches[i]; i++){
35978             coords.push(finger.pageX, finger.pageY);
35979         }
35980         
35981         var x = Math.pow(coords[0] - coords[2], 2);
35982         var y = Math.pow(coords[1] - coords[3], 2);
35983         
35984         this.endDistance = Math.sqrt(x + y);
35985         
35986         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
35987         
35988         if(!this.zoomable()){
35989             this.scale = this.startScale;
35990             return;
35991         }
35992         
35993         this.draw();
35994         
35995     },
35996     
35997     onTouchEnd : function(e)
35998     {
35999         this.pinching = false;
36000         this.dragable = false;
36001         
36002     },
36003     
36004     process : function(file, crop)
36005     {
36006         if(this.loadMask){
36007             this.maskEl.mask(this.loadingText);
36008         }
36009         
36010         this.xhr = new XMLHttpRequest();
36011         
36012         file.xhr = this.xhr;
36013
36014         this.xhr.open(this.method, this.url, true);
36015         
36016         var headers = {
36017             "Accept": "application/json",
36018             "Cache-Control": "no-cache",
36019             "X-Requested-With": "XMLHttpRequest"
36020         };
36021         
36022         for (var headerName in headers) {
36023             var headerValue = headers[headerName];
36024             if (headerValue) {
36025                 this.xhr.setRequestHeader(headerName, headerValue);
36026             }
36027         }
36028         
36029         var _this = this;
36030         
36031         this.xhr.onload = function()
36032         {
36033             _this.xhrOnLoad(_this.xhr);
36034         }
36035         
36036         this.xhr.onerror = function()
36037         {
36038             _this.xhrOnError(_this.xhr);
36039         }
36040         
36041         var formData = new FormData();
36042
36043         formData.append('returnHTML', 'NO');
36044         
36045         if(crop){
36046             formData.append('crop', crop);
36047         }
36048         
36049         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36050             formData.append(this.paramName, file, file.name);
36051         }
36052         
36053         if(typeof(file.filename) != 'undefined'){
36054             formData.append('filename', file.filename);
36055         }
36056         
36057         if(typeof(file.mimetype) != 'undefined'){
36058             formData.append('mimetype', file.mimetype);
36059         }
36060         
36061         if(this.fireEvent('arrange', this, formData) != false){
36062             this.xhr.send(formData);
36063         };
36064     },
36065     
36066     xhrOnLoad : function(xhr)
36067     {
36068         if(this.loadMask){
36069             this.maskEl.unmask();
36070         }
36071         
36072         if (xhr.readyState !== 4) {
36073             this.fireEvent('exception', this, xhr);
36074             return;
36075         }
36076
36077         var response = Roo.decode(xhr.responseText);
36078         
36079         if(!response.success){
36080             this.fireEvent('exception', this, xhr);
36081             return;
36082         }
36083         
36084         var response = Roo.decode(xhr.responseText);
36085         
36086         this.fireEvent('upload', this, response);
36087         
36088     },
36089     
36090     xhrOnError : function()
36091     {
36092         if(this.loadMask){
36093             this.maskEl.unmask();
36094         }
36095         
36096         Roo.log('xhr on error');
36097         
36098         var response = Roo.decode(xhr.responseText);
36099           
36100         Roo.log(response);
36101         
36102     },
36103     
36104     prepare : function(file)
36105     {   
36106         if(this.loadMask){
36107             this.maskEl.mask(this.loadingText);
36108         }
36109         
36110         this.file = false;
36111         this.exif = {};
36112         
36113         if(typeof(file) === 'string'){
36114             this.loadCanvas(file);
36115             return;
36116         }
36117         
36118         if(!file || !this.urlAPI){
36119             return;
36120         }
36121         
36122         this.file = file;
36123         this.cropType = file.type;
36124         
36125         var _this = this;
36126         
36127         if(this.fireEvent('prepare', this, this.file) != false){
36128             
36129             var reader = new FileReader();
36130             
36131             reader.onload = function (e) {
36132                 if (e.target.error) {
36133                     Roo.log(e.target.error);
36134                     return;
36135                 }
36136                 
36137                 var buffer = e.target.result,
36138                     dataView = new DataView(buffer),
36139                     offset = 2,
36140                     maxOffset = dataView.byteLength - 4,
36141                     markerBytes,
36142                     markerLength;
36143                 
36144                 if (dataView.getUint16(0) === 0xffd8) {
36145                     while (offset < maxOffset) {
36146                         markerBytes = dataView.getUint16(offset);
36147                         
36148                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
36149                             markerLength = dataView.getUint16(offset + 2) + 2;
36150                             if (offset + markerLength > dataView.byteLength) {
36151                                 Roo.log('Invalid meta data: Invalid segment size.');
36152                                 break;
36153                             }
36154                             
36155                             if(markerBytes == 0xffe1){
36156                                 _this.parseExifData(
36157                                     dataView,
36158                                     offset,
36159                                     markerLength
36160                                 );
36161                             }
36162                             
36163                             offset += markerLength;
36164                             
36165                             continue;
36166                         }
36167                         
36168                         break;
36169                     }
36170                     
36171                 }
36172                 
36173                 var url = _this.urlAPI.createObjectURL(_this.file);
36174                 
36175                 _this.loadCanvas(url);
36176                 
36177                 return;
36178             }
36179             
36180             reader.readAsArrayBuffer(this.file);
36181             
36182         }
36183         
36184     },
36185     
36186     parseExifData : function(dataView, offset, length)
36187     {
36188         var tiffOffset = offset + 10,
36189             littleEndian,
36190             dirOffset;
36191     
36192         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36193             // No Exif data, might be XMP data instead
36194             return;
36195         }
36196         
36197         // Check for the ASCII code for "Exif" (0x45786966):
36198         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36199             // No Exif data, might be XMP data instead
36200             return;
36201         }
36202         if (tiffOffset + 8 > dataView.byteLength) {
36203             Roo.log('Invalid Exif data: Invalid segment size.');
36204             return;
36205         }
36206         // Check for the two null bytes:
36207         if (dataView.getUint16(offset + 8) !== 0x0000) {
36208             Roo.log('Invalid Exif data: Missing byte alignment offset.');
36209             return;
36210         }
36211         // Check the byte alignment:
36212         switch (dataView.getUint16(tiffOffset)) {
36213         case 0x4949:
36214             littleEndian = true;
36215             break;
36216         case 0x4D4D:
36217             littleEndian = false;
36218             break;
36219         default:
36220             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
36221             return;
36222         }
36223         // Check for the TIFF tag marker (0x002A):
36224         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
36225             Roo.log('Invalid Exif data: Missing TIFF marker.');
36226             return;
36227         }
36228         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
36229         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
36230         
36231         this.parseExifTags(
36232             dataView,
36233             tiffOffset,
36234             tiffOffset + dirOffset,
36235             littleEndian
36236         );
36237     },
36238     
36239     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
36240     {
36241         var tagsNumber,
36242             dirEndOffset,
36243             i;
36244         if (dirOffset + 6 > dataView.byteLength) {
36245             Roo.log('Invalid Exif data: Invalid directory offset.');
36246             return;
36247         }
36248         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
36249         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
36250         if (dirEndOffset + 4 > dataView.byteLength) {
36251             Roo.log('Invalid Exif data: Invalid directory size.');
36252             return;
36253         }
36254         for (i = 0; i < tagsNumber; i += 1) {
36255             this.parseExifTag(
36256                 dataView,
36257                 tiffOffset,
36258                 dirOffset + 2 + 12 * i, // tag offset
36259                 littleEndian
36260             );
36261         }
36262         // Return the offset to the next directory:
36263         return dataView.getUint32(dirEndOffset, littleEndian);
36264     },
36265     
36266     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
36267     {
36268         var tag = dataView.getUint16(offset, littleEndian);
36269         
36270         this.exif[tag] = this.getExifValue(
36271             dataView,
36272             tiffOffset,
36273             offset,
36274             dataView.getUint16(offset + 2, littleEndian), // tag type
36275             dataView.getUint32(offset + 4, littleEndian), // tag length
36276             littleEndian
36277         );
36278     },
36279     
36280     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
36281     {
36282         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
36283             tagSize,
36284             dataOffset,
36285             values,
36286             i,
36287             str,
36288             c;
36289     
36290         if (!tagType) {
36291             Roo.log('Invalid Exif data: Invalid tag type.');
36292             return;
36293         }
36294         
36295         tagSize = tagType.size * length;
36296         // Determine if the value is contained in the dataOffset bytes,
36297         // or if the value at the dataOffset is a pointer to the actual data:
36298         dataOffset = tagSize > 4 ?
36299                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
36300         if (dataOffset + tagSize > dataView.byteLength) {
36301             Roo.log('Invalid Exif data: Invalid data offset.');
36302             return;
36303         }
36304         if (length === 1) {
36305             return tagType.getValue(dataView, dataOffset, littleEndian);
36306         }
36307         values = [];
36308         for (i = 0; i < length; i += 1) {
36309             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
36310         }
36311         
36312         if (tagType.ascii) {
36313             str = '';
36314             // Concatenate the chars:
36315             for (i = 0; i < values.length; i += 1) {
36316                 c = values[i];
36317                 // Ignore the terminating NULL byte(s):
36318                 if (c === '\u0000') {
36319                     break;
36320                 }
36321                 str += c;
36322             }
36323             return str;
36324         }
36325         return values;
36326     }
36327     
36328 });
36329
36330 Roo.apply(Roo.bootstrap.UploadCropbox, {
36331     tags : {
36332         'Orientation': 0x0112
36333     },
36334     
36335     Orientation: {
36336             1: 0, //'top-left',
36337 //            2: 'top-right',
36338             3: 180, //'bottom-right',
36339 //            4: 'bottom-left',
36340 //            5: 'left-top',
36341             6: 90, //'right-top',
36342 //            7: 'right-bottom',
36343             8: 270 //'left-bottom'
36344     },
36345     
36346     exifTagTypes : {
36347         // byte, 8-bit unsigned int:
36348         1: {
36349             getValue: function (dataView, dataOffset) {
36350                 return dataView.getUint8(dataOffset);
36351             },
36352             size: 1
36353         },
36354         // ascii, 8-bit byte:
36355         2: {
36356             getValue: function (dataView, dataOffset) {
36357                 return String.fromCharCode(dataView.getUint8(dataOffset));
36358             },
36359             size: 1,
36360             ascii: true
36361         },
36362         // short, 16 bit int:
36363         3: {
36364             getValue: function (dataView, dataOffset, littleEndian) {
36365                 return dataView.getUint16(dataOffset, littleEndian);
36366             },
36367             size: 2
36368         },
36369         // long, 32 bit int:
36370         4: {
36371             getValue: function (dataView, dataOffset, littleEndian) {
36372                 return dataView.getUint32(dataOffset, littleEndian);
36373             },
36374             size: 4
36375         },
36376         // rational = two long values, first is numerator, second is denominator:
36377         5: {
36378             getValue: function (dataView, dataOffset, littleEndian) {
36379                 return dataView.getUint32(dataOffset, littleEndian) /
36380                     dataView.getUint32(dataOffset + 4, littleEndian);
36381             },
36382             size: 8
36383         },
36384         // slong, 32 bit signed int:
36385         9: {
36386             getValue: function (dataView, dataOffset, littleEndian) {
36387                 return dataView.getInt32(dataOffset, littleEndian);
36388             },
36389             size: 4
36390         },
36391         // srational, two slongs, first is numerator, second is denominator:
36392         10: {
36393             getValue: function (dataView, dataOffset, littleEndian) {
36394                 return dataView.getInt32(dataOffset, littleEndian) /
36395                     dataView.getInt32(dataOffset + 4, littleEndian);
36396             },
36397             size: 8
36398         }
36399     },
36400     
36401     footer : {
36402         STANDARD : [
36403             {
36404                 tag : 'div',
36405                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36406                 action : 'rotate-left',
36407                 cn : [
36408                     {
36409                         tag : 'button',
36410                         cls : 'btn btn-default',
36411                         html : '<i class="fa fa-undo"></i>'
36412                     }
36413                 ]
36414             },
36415             {
36416                 tag : 'div',
36417                 cls : 'btn-group roo-upload-cropbox-picture',
36418                 action : 'picture',
36419                 cn : [
36420                     {
36421                         tag : 'button',
36422                         cls : 'btn btn-default',
36423                         html : '<i class="fa fa-picture-o"></i>'
36424                     }
36425                 ]
36426             },
36427             {
36428                 tag : 'div',
36429                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36430                 action : 'rotate-right',
36431                 cn : [
36432                     {
36433                         tag : 'button',
36434                         cls : 'btn btn-default',
36435                         html : '<i class="fa fa-repeat"></i>'
36436                     }
36437                 ]
36438             }
36439         ],
36440         DOCUMENT : [
36441             {
36442                 tag : 'div',
36443                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36444                 action : 'rotate-left',
36445                 cn : [
36446                     {
36447                         tag : 'button',
36448                         cls : 'btn btn-default',
36449                         html : '<i class="fa fa-undo"></i>'
36450                     }
36451                 ]
36452             },
36453             {
36454                 tag : 'div',
36455                 cls : 'btn-group roo-upload-cropbox-download',
36456                 action : 'download',
36457                 cn : [
36458                     {
36459                         tag : 'button',
36460                         cls : 'btn btn-default',
36461                         html : '<i class="fa fa-download"></i>'
36462                     }
36463                 ]
36464             },
36465             {
36466                 tag : 'div',
36467                 cls : 'btn-group roo-upload-cropbox-crop',
36468                 action : 'crop',
36469                 cn : [
36470                     {
36471                         tag : 'button',
36472                         cls : 'btn btn-default',
36473                         html : '<i class="fa fa-crop"></i>'
36474                     }
36475                 ]
36476             },
36477             {
36478                 tag : 'div',
36479                 cls : 'btn-group roo-upload-cropbox-trash',
36480                 action : 'trash',
36481                 cn : [
36482                     {
36483                         tag : 'button',
36484                         cls : 'btn btn-default',
36485                         html : '<i class="fa fa-trash"></i>'
36486                     }
36487                 ]
36488             },
36489             {
36490                 tag : 'div',
36491                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36492                 action : 'rotate-right',
36493                 cn : [
36494                     {
36495                         tag : 'button',
36496                         cls : 'btn btn-default',
36497                         html : '<i class="fa fa-repeat"></i>'
36498                     }
36499                 ]
36500             }
36501         ],
36502         ROTATOR : [
36503             {
36504                 tag : 'div',
36505                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36506                 action : 'rotate-left',
36507                 cn : [
36508                     {
36509                         tag : 'button',
36510                         cls : 'btn btn-default',
36511                         html : '<i class="fa fa-undo"></i>'
36512                     }
36513                 ]
36514             },
36515             {
36516                 tag : 'div',
36517                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36518                 action : 'rotate-right',
36519                 cn : [
36520                     {
36521                         tag : 'button',
36522                         cls : 'btn btn-default',
36523                         html : '<i class="fa fa-repeat"></i>'
36524                     }
36525                 ]
36526             }
36527         ]
36528     }
36529 });
36530
36531 /*
36532 * Licence: LGPL
36533 */
36534
36535 /**
36536  * @class Roo.bootstrap.DocumentManager
36537  * @extends Roo.bootstrap.Component
36538  * Bootstrap DocumentManager class
36539  * @cfg {String} paramName default 'imageUpload'
36540  * @cfg {String} toolTipName default 'filename'
36541  * @cfg {String} method default POST
36542  * @cfg {String} url action url
36543  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
36544  * @cfg {Boolean} multiple multiple upload default true
36545  * @cfg {Number} thumbSize default 300
36546  * @cfg {String} fieldLabel
36547  * @cfg {Number} labelWidth default 4
36548  * @cfg {String} labelAlign (left|top) default left
36549  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
36550 * @cfg {Number} labellg set the width of label (1-12)
36551  * @cfg {Number} labelmd set the width of label (1-12)
36552  * @cfg {Number} labelsm set the width of label (1-12)
36553  * @cfg {Number} labelxs set the width of label (1-12)
36554  * 
36555  * @constructor
36556  * Create a new DocumentManager
36557  * @param {Object} config The config object
36558  */
36559
36560 Roo.bootstrap.DocumentManager = function(config){
36561     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
36562     
36563     this.files = [];
36564     this.delegates = [];
36565     
36566     this.addEvents({
36567         /**
36568          * @event initial
36569          * Fire when initial the DocumentManager
36570          * @param {Roo.bootstrap.DocumentManager} this
36571          */
36572         "initial" : true,
36573         /**
36574          * @event inspect
36575          * inspect selected file
36576          * @param {Roo.bootstrap.DocumentManager} this
36577          * @param {File} file
36578          */
36579         "inspect" : true,
36580         /**
36581          * @event exception
36582          * Fire when xhr load exception
36583          * @param {Roo.bootstrap.DocumentManager} this
36584          * @param {XMLHttpRequest} xhr
36585          */
36586         "exception" : true,
36587         /**
36588          * @event afterupload
36589          * Fire when xhr load exception
36590          * @param {Roo.bootstrap.DocumentManager} this
36591          * @param {XMLHttpRequest} xhr
36592          */
36593         "afterupload" : true,
36594         /**
36595          * @event prepare
36596          * prepare the form data
36597          * @param {Roo.bootstrap.DocumentManager} this
36598          * @param {Object} formData
36599          */
36600         "prepare" : true,
36601         /**
36602          * @event remove
36603          * Fire when remove the file
36604          * @param {Roo.bootstrap.DocumentManager} this
36605          * @param {Object} file
36606          */
36607         "remove" : true,
36608         /**
36609          * @event refresh
36610          * Fire after refresh the file
36611          * @param {Roo.bootstrap.DocumentManager} this
36612          */
36613         "refresh" : true,
36614         /**
36615          * @event click
36616          * Fire after click the image
36617          * @param {Roo.bootstrap.DocumentManager} this
36618          * @param {Object} file
36619          */
36620         "click" : true,
36621         /**
36622          * @event edit
36623          * Fire when upload a image and editable set to true
36624          * @param {Roo.bootstrap.DocumentManager} this
36625          * @param {Object} file
36626          */
36627         "edit" : true,
36628         /**
36629          * @event beforeselectfile
36630          * Fire before select file
36631          * @param {Roo.bootstrap.DocumentManager} this
36632          */
36633         "beforeselectfile" : true,
36634         /**
36635          * @event process
36636          * Fire before process file
36637          * @param {Roo.bootstrap.DocumentManager} this
36638          * @param {Object} file
36639          */
36640         "process" : true,
36641         /**
36642          * @event previewrendered
36643          * Fire when preview rendered
36644          * @param {Roo.bootstrap.DocumentManager} this
36645          * @param {Object} file
36646          */
36647         "previewrendered" : true,
36648         /**
36649          */
36650         "previewResize" : true
36651         
36652     });
36653 };
36654
36655 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
36656     
36657     boxes : 0,
36658     inputName : '',
36659     thumbSize : 300,
36660     multiple : true,
36661     files : false,
36662     method : 'POST',
36663     url : '',
36664     paramName : 'imageUpload',
36665     toolTipName : 'filename',
36666     fieldLabel : '',
36667     labelWidth : 4,
36668     labelAlign : 'left',
36669     editable : true,
36670     delegates : false,
36671     xhr : false, 
36672     
36673     labellg : 0,
36674     labelmd : 0,
36675     labelsm : 0,
36676     labelxs : 0,
36677     
36678     getAutoCreate : function()
36679     {   
36680         var managerWidget = {
36681             tag : 'div',
36682             cls : 'roo-document-manager',
36683             cn : [
36684                 {
36685                     tag : 'input',
36686                     cls : 'roo-document-manager-selector',
36687                     type : 'file'
36688                 },
36689                 {
36690                     tag : 'div',
36691                     cls : 'roo-document-manager-uploader',
36692                     cn : [
36693                         {
36694                             tag : 'div',
36695                             cls : 'roo-document-manager-upload-btn',
36696                             html : '<i class="fa fa-plus"></i>'
36697                         }
36698                     ]
36699                     
36700                 }
36701             ]
36702         };
36703         
36704         var content = [
36705             {
36706                 tag : 'div',
36707                 cls : 'column col-md-12',
36708                 cn : managerWidget
36709             }
36710         ];
36711         
36712         if(this.fieldLabel.length){
36713             
36714             content = [
36715                 {
36716                     tag : 'div',
36717                     cls : 'column col-md-12',
36718                     html : this.fieldLabel
36719                 },
36720                 {
36721                     tag : 'div',
36722                     cls : 'column col-md-12',
36723                     cn : managerWidget
36724                 }
36725             ];
36726
36727             if(this.labelAlign == 'left'){
36728                 content = [
36729                     {
36730                         tag : 'div',
36731                         cls : 'column',
36732                         html : this.fieldLabel
36733                     },
36734                     {
36735                         tag : 'div',
36736                         cls : 'column',
36737                         cn : managerWidget
36738                     }
36739                 ];
36740                 
36741                 if(this.labelWidth > 12){
36742                     content[0].style = "width: " + this.labelWidth + 'px';
36743                 }
36744
36745                 if(this.labelWidth < 13 && this.labelmd == 0){
36746                     this.labelmd = this.labelWidth;
36747                 }
36748
36749                 if(this.labellg > 0){
36750                     content[0].cls += ' col-lg-' + this.labellg;
36751                     content[1].cls += ' col-lg-' + (12 - this.labellg);
36752                 }
36753
36754                 if(this.labelmd > 0){
36755                     content[0].cls += ' col-md-' + this.labelmd;
36756                     content[1].cls += ' col-md-' + (12 - this.labelmd);
36757                 }
36758
36759                 if(this.labelsm > 0){
36760                     content[0].cls += ' col-sm-' + this.labelsm;
36761                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
36762                 }
36763
36764                 if(this.labelxs > 0){
36765                     content[0].cls += ' col-xs-' + this.labelxs;
36766                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
36767                 }
36768                 
36769             }
36770         }
36771         
36772         var cfg = {
36773             tag : 'div',
36774             cls : 'row clearfix',
36775             cn : content
36776         };
36777         
36778         return cfg;
36779         
36780     },
36781     
36782     initEvents : function()
36783     {
36784         this.managerEl = this.el.select('.roo-document-manager', true).first();
36785         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36786         
36787         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
36788         this.selectorEl.hide();
36789         
36790         if(this.multiple){
36791             this.selectorEl.attr('multiple', 'multiple');
36792         }
36793         
36794         this.selectorEl.on('change', this.onFileSelected, this);
36795         
36796         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
36797         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36798         
36799         this.uploader.on('click', this.onUploaderClick, this);
36800         
36801         this.renderProgressDialog();
36802         
36803         var _this = this;
36804         
36805         window.addEventListener("resize", function() { _this.refresh(); } );
36806         
36807         this.fireEvent('initial', this);
36808     },
36809     
36810     renderProgressDialog : function()
36811     {
36812         var _this = this;
36813         
36814         this.progressDialog = new Roo.bootstrap.Modal({
36815             cls : 'roo-document-manager-progress-dialog',
36816             allow_close : false,
36817             animate : false,
36818             title : '',
36819             buttons : [
36820                 {
36821                     name  :'cancel',
36822                     weight : 'danger',
36823                     html : 'Cancel'
36824                 }
36825             ], 
36826             listeners : { 
36827                 btnclick : function() {
36828                     _this.uploadCancel();
36829                     this.hide();
36830                 }
36831             }
36832         });
36833          
36834         this.progressDialog.render(Roo.get(document.body));
36835          
36836         this.progress = new Roo.bootstrap.Progress({
36837             cls : 'roo-document-manager-progress',
36838             active : true,
36839             striped : true
36840         });
36841         
36842         this.progress.render(this.progressDialog.getChildContainer());
36843         
36844         this.progressBar = new Roo.bootstrap.ProgressBar({
36845             cls : 'roo-document-manager-progress-bar',
36846             aria_valuenow : 0,
36847             aria_valuemin : 0,
36848             aria_valuemax : 12,
36849             panel : 'success'
36850         });
36851         
36852         this.progressBar.render(this.progress.getChildContainer());
36853     },
36854     
36855     onUploaderClick : function(e)
36856     {
36857         e.preventDefault();
36858      
36859         if(this.fireEvent('beforeselectfile', this) != false){
36860             this.selectorEl.dom.click();
36861         }
36862         
36863     },
36864     
36865     onFileSelected : function(e)
36866     {
36867         e.preventDefault();
36868         
36869         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36870             return;
36871         }
36872         
36873         Roo.each(this.selectorEl.dom.files, function(file){
36874             if(this.fireEvent('inspect', this, file) != false){
36875                 this.files.push(file);
36876             }
36877         }, this);
36878         
36879         this.queue();
36880         
36881     },
36882     
36883     queue : function()
36884     {
36885         this.selectorEl.dom.value = '';
36886         
36887         if(!this.files || !this.files.length){
36888             return;
36889         }
36890         
36891         if(this.boxes > 0 && this.files.length > this.boxes){
36892             this.files = this.files.slice(0, this.boxes);
36893         }
36894         
36895         this.uploader.show();
36896         
36897         if(this.boxes > 0 && this.files.length > this.boxes - 1){
36898             this.uploader.hide();
36899         }
36900         
36901         var _this = this;
36902         
36903         var files = [];
36904         
36905         var docs = [];
36906         
36907         Roo.each(this.files, function(file){
36908             
36909             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
36910                 var f = this.renderPreview(file);
36911                 files.push(f);
36912                 return;
36913             }
36914             
36915             if(file.type.indexOf('image') != -1){
36916                 this.delegates.push(
36917                     (function(){
36918                         _this.process(file);
36919                     }).createDelegate(this)
36920                 );
36921         
36922                 return;
36923             }
36924             
36925             docs.push(
36926                 (function(){
36927                     _this.process(file);
36928                 }).createDelegate(this)
36929             );
36930             
36931         }, this);
36932         
36933         this.files = files;
36934         
36935         this.delegates = this.delegates.concat(docs);
36936         
36937         if(!this.delegates.length){
36938             this.refresh();
36939             return;
36940         }
36941         
36942         this.progressBar.aria_valuemax = this.delegates.length;
36943         
36944         this.arrange();
36945         
36946         return;
36947     },
36948     
36949     arrange : function()
36950     {
36951         if(!this.delegates.length){
36952             this.progressDialog.hide();
36953             this.refresh();
36954             return;
36955         }
36956         
36957         var delegate = this.delegates.shift();
36958         
36959         this.progressDialog.show();
36960         
36961         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
36962         
36963         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
36964         
36965         delegate();
36966     },
36967     
36968     refresh : function()
36969     {
36970         this.uploader.show();
36971         
36972         if(this.boxes > 0 && this.files.length > this.boxes - 1){
36973             this.uploader.hide();
36974         }
36975         
36976         Roo.isTouch ? this.closable(false) : this.closable(true);
36977         
36978         this.fireEvent('refresh', this);
36979     },
36980     
36981     onRemove : function(e, el, o)
36982     {
36983         e.preventDefault();
36984         
36985         this.fireEvent('remove', this, o);
36986         
36987     },
36988     
36989     remove : function(o)
36990     {
36991         var files = [];
36992         
36993         Roo.each(this.files, function(file){
36994             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
36995                 files.push(file);
36996                 return;
36997             }
36998
36999             o.target.remove();
37000
37001         }, this);
37002         
37003         this.files = files;
37004         
37005         this.refresh();
37006     },
37007     
37008     clear : function()
37009     {
37010         Roo.each(this.files, function(file){
37011             if(!file.target){
37012                 return;
37013             }
37014             
37015             file.target.remove();
37016
37017         }, this);
37018         
37019         this.files = [];
37020         
37021         this.refresh();
37022     },
37023     
37024     onClick : function(e, el, o)
37025     {
37026         e.preventDefault();
37027         
37028         this.fireEvent('click', this, o);
37029         
37030     },
37031     
37032     closable : function(closable)
37033     {
37034         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37035             
37036             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37037             
37038             if(closable){
37039                 el.show();
37040                 return;
37041             }
37042             
37043             el.hide();
37044             
37045         }, this);
37046     },
37047     
37048     xhrOnLoad : function(xhr)
37049     {
37050         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37051             el.remove();
37052         }, this);
37053         
37054         if (xhr.readyState !== 4) {
37055             this.arrange();
37056             this.fireEvent('exception', this, xhr);
37057             return;
37058         }
37059
37060         var response = Roo.decode(xhr.responseText);
37061         
37062         if(!response.success){
37063             this.arrange();
37064             this.fireEvent('exception', this, xhr);
37065             return;
37066         }
37067         
37068         var file = this.renderPreview(response.data);
37069         
37070         this.files.push(file);
37071         
37072         this.arrange();
37073         
37074         this.fireEvent('afterupload', this, xhr);
37075         
37076     },
37077     
37078     xhrOnError : function(xhr)
37079     {
37080         Roo.log('xhr on error');
37081         
37082         var response = Roo.decode(xhr.responseText);
37083           
37084         Roo.log(response);
37085         
37086         this.arrange();
37087     },
37088     
37089     process : function(file)
37090     {
37091         if(this.fireEvent('process', this, file) !== false){
37092             if(this.editable && file.type.indexOf('image') != -1){
37093                 this.fireEvent('edit', this, file);
37094                 return;
37095             }
37096
37097             this.uploadStart(file, false);
37098
37099             return;
37100         }
37101         
37102     },
37103     
37104     uploadStart : function(file, crop)
37105     {
37106         this.xhr = new XMLHttpRequest();
37107         
37108         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37109             this.arrange();
37110             return;
37111         }
37112         
37113         file.xhr = this.xhr;
37114             
37115         this.managerEl.createChild({
37116             tag : 'div',
37117             cls : 'roo-document-manager-loading',
37118             cn : [
37119                 {
37120                     tag : 'div',
37121                     tooltip : file.name,
37122                     cls : 'roo-document-manager-thumb',
37123                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37124                 }
37125             ]
37126
37127         });
37128
37129         this.xhr.open(this.method, this.url, true);
37130         
37131         var headers = {
37132             "Accept": "application/json",
37133             "Cache-Control": "no-cache",
37134             "X-Requested-With": "XMLHttpRequest"
37135         };
37136         
37137         for (var headerName in headers) {
37138             var headerValue = headers[headerName];
37139             if (headerValue) {
37140                 this.xhr.setRequestHeader(headerName, headerValue);
37141             }
37142         }
37143         
37144         var _this = this;
37145         
37146         this.xhr.onload = function()
37147         {
37148             _this.xhrOnLoad(_this.xhr);
37149         }
37150         
37151         this.xhr.onerror = function()
37152         {
37153             _this.xhrOnError(_this.xhr);
37154         }
37155         
37156         var formData = new FormData();
37157
37158         formData.append('returnHTML', 'NO');
37159         
37160         if(crop){
37161             formData.append('crop', crop);
37162         }
37163         
37164         formData.append(this.paramName, file, file.name);
37165         
37166         var options = {
37167             file : file, 
37168             manually : false
37169         };
37170         
37171         if(this.fireEvent('prepare', this, formData, options) != false){
37172             
37173             if(options.manually){
37174                 return;
37175             }
37176             
37177             this.xhr.send(formData);
37178             return;
37179         };
37180         
37181         this.uploadCancel();
37182     },
37183     
37184     uploadCancel : function()
37185     {
37186         if (this.xhr) {
37187             this.xhr.abort();
37188         }
37189         
37190         this.delegates = [];
37191         
37192         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37193             el.remove();
37194         }, this);
37195         
37196         this.arrange();
37197     },
37198     
37199     renderPreview : function(file)
37200     {
37201         if(typeof(file.target) != 'undefined' && file.target){
37202             return file;
37203         }
37204         
37205         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
37206         
37207         var previewEl = this.managerEl.createChild({
37208             tag : 'div',
37209             cls : 'roo-document-manager-preview',
37210             cn : [
37211                 {
37212                     tag : 'div',
37213                     tooltip : file[this.toolTipName],
37214                     cls : 'roo-document-manager-thumb',
37215                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
37216                 },
37217                 {
37218                     tag : 'button',
37219                     cls : 'close',
37220                     html : '<i class="fa fa-times-circle"></i>'
37221                 }
37222             ]
37223         });
37224
37225         var close = previewEl.select('button.close', true).first();
37226
37227         close.on('click', this.onRemove, this, file);
37228
37229         file.target = previewEl;
37230
37231         var image = previewEl.select('img', true).first();
37232         
37233         var _this = this;
37234         
37235         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
37236         
37237         image.on('click', this.onClick, this, file);
37238         
37239         this.fireEvent('previewrendered', this, file);
37240         
37241         return file;
37242         
37243     },
37244     
37245     onPreviewLoad : function(file, image)
37246     {
37247         if(typeof(file.target) == 'undefined' || !file.target){
37248             return;
37249         }
37250         
37251         var width = image.dom.naturalWidth || image.dom.width;
37252         var height = image.dom.naturalHeight || image.dom.height;
37253         
37254         if(!this.previewResize) {
37255             return;
37256         }
37257         
37258         if(width > height){
37259             file.target.addClass('wide');
37260             return;
37261         }
37262         
37263         file.target.addClass('tall');
37264         return;
37265         
37266     },
37267     
37268     uploadFromSource : function(file, crop)
37269     {
37270         this.xhr = new XMLHttpRequest();
37271         
37272         this.managerEl.createChild({
37273             tag : 'div',
37274             cls : 'roo-document-manager-loading',
37275             cn : [
37276                 {
37277                     tag : 'div',
37278                     tooltip : file.name,
37279                     cls : 'roo-document-manager-thumb',
37280                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37281                 }
37282             ]
37283
37284         });
37285
37286         this.xhr.open(this.method, this.url, true);
37287         
37288         var headers = {
37289             "Accept": "application/json",
37290             "Cache-Control": "no-cache",
37291             "X-Requested-With": "XMLHttpRequest"
37292         };
37293         
37294         for (var headerName in headers) {
37295             var headerValue = headers[headerName];
37296             if (headerValue) {
37297                 this.xhr.setRequestHeader(headerName, headerValue);
37298             }
37299         }
37300         
37301         var _this = this;
37302         
37303         this.xhr.onload = function()
37304         {
37305             _this.xhrOnLoad(_this.xhr);
37306         }
37307         
37308         this.xhr.onerror = function()
37309         {
37310             _this.xhrOnError(_this.xhr);
37311         }
37312         
37313         var formData = new FormData();
37314
37315         formData.append('returnHTML', 'NO');
37316         
37317         formData.append('crop', crop);
37318         
37319         if(typeof(file.filename) != 'undefined'){
37320             formData.append('filename', file.filename);
37321         }
37322         
37323         if(typeof(file.mimetype) != 'undefined'){
37324             formData.append('mimetype', file.mimetype);
37325         }
37326         
37327         Roo.log(formData);
37328         
37329         if(this.fireEvent('prepare', this, formData) != false){
37330             this.xhr.send(formData);
37331         };
37332     }
37333 });
37334
37335 /*
37336 * Licence: LGPL
37337 */
37338
37339 /**
37340  * @class Roo.bootstrap.DocumentViewer
37341  * @extends Roo.bootstrap.Component
37342  * Bootstrap DocumentViewer class
37343  * @cfg {Boolean} showDownload (true|false) show download button (default true)
37344  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
37345  * 
37346  * @constructor
37347  * Create a new DocumentViewer
37348  * @param {Object} config The config object
37349  */
37350
37351 Roo.bootstrap.DocumentViewer = function(config){
37352     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
37353     
37354     this.addEvents({
37355         /**
37356          * @event initial
37357          * Fire after initEvent
37358          * @param {Roo.bootstrap.DocumentViewer} this
37359          */
37360         "initial" : true,
37361         /**
37362          * @event click
37363          * Fire after click
37364          * @param {Roo.bootstrap.DocumentViewer} this
37365          */
37366         "click" : true,
37367         /**
37368          * @event download
37369          * Fire after download button
37370          * @param {Roo.bootstrap.DocumentViewer} this
37371          */
37372         "download" : true,
37373         /**
37374          * @event trash
37375          * Fire after trash button
37376          * @param {Roo.bootstrap.DocumentViewer} this
37377          */
37378         "trash" : true
37379         
37380     });
37381 };
37382
37383 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
37384     
37385     showDownload : true,
37386     
37387     showTrash : true,
37388     
37389     getAutoCreate : function()
37390     {
37391         var cfg = {
37392             tag : 'div',
37393             cls : 'roo-document-viewer',
37394             cn : [
37395                 {
37396                     tag : 'div',
37397                     cls : 'roo-document-viewer-body',
37398                     cn : [
37399                         {
37400                             tag : 'div',
37401                             cls : 'roo-document-viewer-thumb',
37402                             cn : [
37403                                 {
37404                                     tag : 'img',
37405                                     cls : 'roo-document-viewer-image'
37406                                 }
37407                             ]
37408                         }
37409                     ]
37410                 },
37411                 {
37412                     tag : 'div',
37413                     cls : 'roo-document-viewer-footer',
37414                     cn : {
37415                         tag : 'div',
37416                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
37417                         cn : [
37418                             {
37419                                 tag : 'div',
37420                                 cls : 'btn-group roo-document-viewer-download',
37421                                 cn : [
37422                                     {
37423                                         tag : 'button',
37424                                         cls : 'btn btn-default',
37425                                         html : '<i class="fa fa-download"></i>'
37426                                     }
37427                                 ]
37428                             },
37429                             {
37430                                 tag : 'div',
37431                                 cls : 'btn-group roo-document-viewer-trash',
37432                                 cn : [
37433                                     {
37434                                         tag : 'button',
37435                                         cls : 'btn btn-default',
37436                                         html : '<i class="fa fa-trash"></i>'
37437                                     }
37438                                 ]
37439                             }
37440                         ]
37441                     }
37442                 }
37443             ]
37444         };
37445         
37446         return cfg;
37447     },
37448     
37449     initEvents : function()
37450     {
37451         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
37452         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37453         
37454         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
37455         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37456         
37457         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
37458         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37459         
37460         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
37461         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
37462         
37463         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
37464         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
37465         
37466         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
37467         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
37468         
37469         this.bodyEl.on('click', this.onClick, this);
37470         this.downloadBtn.on('click', this.onDownload, this);
37471         this.trashBtn.on('click', this.onTrash, this);
37472         
37473         this.downloadBtn.hide();
37474         this.trashBtn.hide();
37475         
37476         if(this.showDownload){
37477             this.downloadBtn.show();
37478         }
37479         
37480         if(this.showTrash){
37481             this.trashBtn.show();
37482         }
37483         
37484         if(!this.showDownload && !this.showTrash) {
37485             this.footerEl.hide();
37486         }
37487         
37488     },
37489     
37490     initial : function()
37491     {
37492         this.fireEvent('initial', this);
37493         
37494     },
37495     
37496     onClick : function(e)
37497     {
37498         e.preventDefault();
37499         
37500         this.fireEvent('click', this);
37501     },
37502     
37503     onDownload : function(e)
37504     {
37505         e.preventDefault();
37506         
37507         this.fireEvent('download', this);
37508     },
37509     
37510     onTrash : function(e)
37511     {
37512         e.preventDefault();
37513         
37514         this.fireEvent('trash', this);
37515     }
37516     
37517 });
37518 /*
37519  * - LGPL
37520  *
37521  * FieldLabel
37522  * 
37523  */
37524
37525 /**
37526  * @class Roo.bootstrap.form.FieldLabel
37527  * @extends Roo.bootstrap.Component
37528  * Bootstrap FieldLabel class
37529  * @cfg {String} html contents of the element
37530  * @cfg {String} tag tag of the element default label
37531  * @cfg {String} cls class of the element
37532  * @cfg {String} target label target 
37533  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
37534  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
37535  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
37536  * @cfg {String} iconTooltip default "This field is required"
37537  * @cfg {String} indicatorpos (left|right) default left
37538  * 
37539  * @constructor
37540  * Create a new FieldLabel
37541  * @param {Object} config The config object
37542  */
37543
37544 Roo.bootstrap.form.FieldLabel = function(config){
37545     Roo.bootstrap.Element.superclass.constructor.call(this, config);
37546     
37547     this.addEvents({
37548             /**
37549              * @event invalid
37550              * Fires after the field has been marked as invalid.
37551              * @param {Roo.form.FieldLabel} this
37552              * @param {String} msg The validation message
37553              */
37554             invalid : true,
37555             /**
37556              * @event valid
37557              * Fires after the field has been validated with no errors.
37558              * @param {Roo.form.FieldLabel} this
37559              */
37560             valid : true
37561         });
37562 };
37563
37564 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
37565     
37566     tag: 'label',
37567     cls: '',
37568     html: '',
37569     target: '',
37570     allowBlank : true,
37571     invalidClass : 'has-warning',
37572     validClass : 'has-success',
37573     iconTooltip : 'This field is required',
37574     indicatorpos : 'left',
37575     
37576     getAutoCreate : function(){
37577         
37578         var cls = "";
37579         if (!this.allowBlank) {
37580             cls  = "visible";
37581         }
37582         
37583         var cfg = {
37584             tag : this.tag,
37585             cls : 'roo-bootstrap-field-label ' + this.cls,
37586             for : this.target,
37587             cn : [
37588                 {
37589                     tag : 'i',
37590                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
37591                     tooltip : this.iconTooltip
37592                 },
37593                 {
37594                     tag : 'span',
37595                     html : this.html
37596                 }
37597             ] 
37598         };
37599         
37600         if(this.indicatorpos == 'right'){
37601             var cfg = {
37602                 tag : this.tag,
37603                 cls : 'roo-bootstrap-field-label ' + this.cls,
37604                 for : this.target,
37605                 cn : [
37606                     {
37607                         tag : 'span',
37608                         html : this.html
37609                     },
37610                     {
37611                         tag : 'i',
37612                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
37613                         tooltip : this.iconTooltip
37614                     }
37615                 ] 
37616             };
37617         }
37618         
37619         return cfg;
37620     },
37621     
37622     initEvents: function() 
37623     {
37624         Roo.bootstrap.Element.superclass.initEvents.call(this);
37625         
37626         this.indicator = this.indicatorEl();
37627         
37628         if(this.indicator){
37629             this.indicator.removeClass('visible');
37630             this.indicator.addClass('invisible');
37631         }
37632         
37633         Roo.bootstrap.form.FieldLabel.register(this);
37634     },
37635     
37636     indicatorEl : function()
37637     {
37638         var indicator = this.el.select('i.roo-required-indicator',true).first();
37639         
37640         if(!indicator){
37641             return false;
37642         }
37643         
37644         return indicator;
37645         
37646     },
37647     
37648     /**
37649      * Mark this field as valid
37650      */
37651     markValid : function()
37652     {
37653         if(this.indicator){
37654             this.indicator.removeClass('visible');
37655             this.indicator.addClass('invisible');
37656         }
37657         if (Roo.bootstrap.version == 3) {
37658             this.el.removeClass(this.invalidClass);
37659             this.el.addClass(this.validClass);
37660         } else {
37661             this.el.removeClass('is-invalid');
37662             this.el.addClass('is-valid');
37663         }
37664         
37665         
37666         this.fireEvent('valid', this);
37667     },
37668     
37669     /**
37670      * Mark this field as invalid
37671      * @param {String} msg The validation message
37672      */
37673     markInvalid : function(msg)
37674     {
37675         if(this.indicator){
37676             this.indicator.removeClass('invisible');
37677             this.indicator.addClass('visible');
37678         }
37679           if (Roo.bootstrap.version == 3) {
37680             this.el.removeClass(this.validClass);
37681             this.el.addClass(this.invalidClass);
37682         } else {
37683             this.el.removeClass('is-valid');
37684             this.el.addClass('is-invalid');
37685         }
37686         
37687         
37688         this.fireEvent('invalid', this, msg);
37689     }
37690     
37691    
37692 });
37693
37694 Roo.apply(Roo.bootstrap.form.FieldLabel, {
37695     
37696     groups: {},
37697     
37698      /**
37699     * register a FieldLabel Group
37700     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
37701     */
37702     register : function(label)
37703     {
37704         if(this.groups.hasOwnProperty(label.target)){
37705             return;
37706         }
37707      
37708         this.groups[label.target] = label;
37709         
37710     },
37711     /**
37712     * fetch a FieldLabel Group based on the target
37713     * @param {string} target
37714     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
37715     */
37716     get: function(target) {
37717         if (typeof(this.groups[target]) == 'undefined') {
37718             return false;
37719         }
37720         
37721         return this.groups[target] ;
37722     }
37723 });
37724
37725  
37726
37727  /*
37728  * - LGPL
37729  *
37730  * page DateSplitField.
37731  * 
37732  */
37733
37734
37735 /**
37736  * @class Roo.bootstrap.form.DateSplitField
37737  * @extends Roo.bootstrap.Component
37738  * Bootstrap DateSplitField class
37739  * @cfg {string} fieldLabel - the label associated
37740  * @cfg {Number} labelWidth set the width of label (0-12)
37741  * @cfg {String} labelAlign (top|left)
37742  * @cfg {Boolean} dayAllowBlank (true|false) default false
37743  * @cfg {Boolean} monthAllowBlank (true|false) default false
37744  * @cfg {Boolean} yearAllowBlank (true|false) default false
37745  * @cfg {string} dayPlaceholder 
37746  * @cfg {string} monthPlaceholder
37747  * @cfg {string} yearPlaceholder
37748  * @cfg {string} dayFormat default 'd'
37749  * @cfg {string} monthFormat default 'm'
37750  * @cfg {string} yearFormat default 'Y'
37751  * @cfg {Number} labellg set the width of label (1-12)
37752  * @cfg {Number} labelmd set the width of label (1-12)
37753  * @cfg {Number} labelsm set the width of label (1-12)
37754  * @cfg {Number} labelxs set the width of label (1-12)
37755
37756  *     
37757  * @constructor
37758  * Create a new DateSplitField
37759  * @param {Object} config The config object
37760  */
37761
37762 Roo.bootstrap.form.DateSplitField = function(config){
37763     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
37764     
37765     this.addEvents({
37766         // raw events
37767          /**
37768          * @event years
37769          * getting the data of years
37770          * @param {Roo.bootstrap.form.DateSplitField} this
37771          * @param {Object} years
37772          */
37773         "years" : true,
37774         /**
37775          * @event days
37776          * getting the data of days
37777          * @param {Roo.bootstrap.form.DateSplitField} this
37778          * @param {Object} days
37779          */
37780         "days" : true,
37781         /**
37782          * @event invalid
37783          * Fires after the field has been marked as invalid.
37784          * @param {Roo.form.Field} this
37785          * @param {String} msg The validation message
37786          */
37787         invalid : true,
37788        /**
37789          * @event valid
37790          * Fires after the field has been validated with no errors.
37791          * @param {Roo.form.Field} this
37792          */
37793         valid : true
37794     });
37795 };
37796
37797 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
37798     
37799     fieldLabel : '',
37800     labelAlign : 'top',
37801     labelWidth : 3,
37802     dayAllowBlank : false,
37803     monthAllowBlank : false,
37804     yearAllowBlank : false,
37805     dayPlaceholder : '',
37806     monthPlaceholder : '',
37807     yearPlaceholder : '',
37808     dayFormat : 'd',
37809     monthFormat : 'm',
37810     yearFormat : 'Y',
37811     isFormField : true,
37812     labellg : 0,
37813     labelmd : 0,
37814     labelsm : 0,
37815     labelxs : 0,
37816     
37817     getAutoCreate : function()
37818     {
37819         var cfg = {
37820             tag : 'div',
37821             cls : 'row roo-date-split-field-group',
37822             cn : [
37823                 {
37824                     tag : 'input',
37825                     type : 'hidden',
37826                     cls : 'form-hidden-field roo-date-split-field-group-value',
37827                     name : this.name
37828                 }
37829             ]
37830         };
37831         
37832         var labelCls = 'col-md-12';
37833         var contentCls = 'col-md-4';
37834         
37835         if(this.fieldLabel){
37836             
37837             var label = {
37838                 tag : 'div',
37839                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
37840                 cn : [
37841                     {
37842                         tag : 'label',
37843                         html : this.fieldLabel
37844                     }
37845                 ]
37846             };
37847             
37848             if(this.labelAlign == 'left'){
37849             
37850                 if(this.labelWidth > 12){
37851                     label.style = "width: " + this.labelWidth + 'px';
37852                 }
37853
37854                 if(this.labelWidth < 13 && this.labelmd == 0){
37855                     this.labelmd = this.labelWidth;
37856                 }
37857
37858                 if(this.labellg > 0){
37859                     labelCls = ' col-lg-' + this.labellg;
37860                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
37861                 }
37862
37863                 if(this.labelmd > 0){
37864                     labelCls = ' col-md-' + this.labelmd;
37865                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
37866                 }
37867
37868                 if(this.labelsm > 0){
37869                     labelCls = ' col-sm-' + this.labelsm;
37870                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
37871                 }
37872
37873                 if(this.labelxs > 0){
37874                     labelCls = ' col-xs-' + this.labelxs;
37875                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
37876                 }
37877             }
37878             
37879             label.cls += ' ' + labelCls;
37880             
37881             cfg.cn.push(label);
37882         }
37883         
37884         Roo.each(['day', 'month', 'year'], function(t){
37885             cfg.cn.push({
37886                 tag : 'div',
37887                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
37888             });
37889         }, this);
37890         
37891         return cfg;
37892     },
37893     
37894     inputEl: function ()
37895     {
37896         return this.el.select('.roo-date-split-field-group-value', true).first();
37897     },
37898     
37899     onRender : function(ct, position) 
37900     {
37901         var _this = this;
37902         
37903         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
37904         
37905         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
37906         
37907         this.dayField = new Roo.bootstrap.form.ComboBox({
37908             allowBlank : this.dayAllowBlank,
37909             alwaysQuery : true,
37910             displayField : 'value',
37911             editable : false,
37912             fieldLabel : '',
37913             forceSelection : true,
37914             mode : 'local',
37915             placeholder : this.dayPlaceholder,
37916             selectOnFocus : true,
37917             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
37918             triggerAction : 'all',
37919             typeAhead : true,
37920             valueField : 'value',
37921             store : new Roo.data.SimpleStore({
37922                 data : (function() {    
37923                     var days = [];
37924                     _this.fireEvent('days', _this, days);
37925                     return days;
37926                 })(),
37927                 fields : [ 'value' ]
37928             }),
37929             listeners : {
37930                 select : function (_self, record, index)
37931                 {
37932                     _this.setValue(_this.getValue());
37933                 }
37934             }
37935         });
37936
37937         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
37938         
37939         this.monthField = new Roo.bootstrap.form.MonthField({
37940             after : '<i class=\"fa fa-calendar\"></i>',
37941             allowBlank : this.monthAllowBlank,
37942             placeholder : this.monthPlaceholder,
37943             readOnly : true,
37944             listeners : {
37945                 render : function (_self)
37946                 {
37947                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
37948                         e.preventDefault();
37949                         _self.focus();
37950                     });
37951                 },
37952                 select : function (_self, oldvalue, newvalue)
37953                 {
37954                     _this.setValue(_this.getValue());
37955                 }
37956             }
37957         });
37958         
37959         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
37960         
37961         this.yearField = new Roo.bootstrap.form.ComboBox({
37962             allowBlank : this.yearAllowBlank,
37963             alwaysQuery : true,
37964             displayField : 'value',
37965             editable : false,
37966             fieldLabel : '',
37967             forceSelection : true,
37968             mode : 'local',
37969             placeholder : this.yearPlaceholder,
37970             selectOnFocus : true,
37971             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
37972             triggerAction : 'all',
37973             typeAhead : true,
37974             valueField : 'value',
37975             store : new Roo.data.SimpleStore({
37976                 data : (function() {
37977                     var years = [];
37978                     _this.fireEvent('years', _this, years);
37979                     return years;
37980                 })(),
37981                 fields : [ 'value' ]
37982             }),
37983             listeners : {
37984                 select : function (_self, record, index)
37985                 {
37986                     _this.setValue(_this.getValue());
37987                 }
37988             }
37989         });
37990
37991         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
37992     },
37993     
37994     setValue : function(v, format)
37995     {
37996         this.inputEl.dom.value = v;
37997         
37998         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
37999         
38000         var d = Date.parseDate(v, f);
38001         
38002         if(!d){
38003             this.validate();
38004             return;
38005         }
38006         
38007         this.setDay(d.format(this.dayFormat));
38008         this.setMonth(d.format(this.monthFormat));
38009         this.setYear(d.format(this.yearFormat));
38010         
38011         this.validate();
38012         
38013         return;
38014     },
38015     
38016     setDay : function(v)
38017     {
38018         this.dayField.setValue(v);
38019         this.inputEl.dom.value = this.getValue();
38020         this.validate();
38021         return;
38022     },
38023     
38024     setMonth : function(v)
38025     {
38026         this.monthField.setValue(v, true);
38027         this.inputEl.dom.value = this.getValue();
38028         this.validate();
38029         return;
38030     },
38031     
38032     setYear : function(v)
38033     {
38034         this.yearField.setValue(v);
38035         this.inputEl.dom.value = this.getValue();
38036         this.validate();
38037         return;
38038     },
38039     
38040     getDay : function()
38041     {
38042         return this.dayField.getValue();
38043     },
38044     
38045     getMonth : function()
38046     {
38047         return this.monthField.getValue();
38048     },
38049     
38050     getYear : function()
38051     {
38052         return this.yearField.getValue();
38053     },
38054     
38055     getValue : function()
38056     {
38057         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38058         
38059         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38060         
38061         return date;
38062     },
38063     
38064     reset : function()
38065     {
38066         this.setDay('');
38067         this.setMonth('');
38068         this.setYear('');
38069         this.inputEl.dom.value = '';
38070         this.validate();
38071         return;
38072     },
38073     
38074     validate : function()
38075     {
38076         var d = this.dayField.validate();
38077         var m = this.monthField.validate();
38078         var y = this.yearField.validate();
38079         
38080         var valid = true;
38081         
38082         if(
38083                 (!this.dayAllowBlank && !d) ||
38084                 (!this.monthAllowBlank && !m) ||
38085                 (!this.yearAllowBlank && !y)
38086         ){
38087             valid = false;
38088         }
38089         
38090         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38091             return valid;
38092         }
38093         
38094         if(valid){
38095             this.markValid();
38096             return valid;
38097         }
38098         
38099         this.markInvalid();
38100         
38101         return valid;
38102     },
38103     
38104     markValid : function()
38105     {
38106         
38107         var label = this.el.select('label', true).first();
38108         var icon = this.el.select('i.fa-star', true).first();
38109
38110         if(label && icon){
38111             icon.remove();
38112         }
38113         
38114         this.fireEvent('valid', this);
38115     },
38116     
38117      /**
38118      * Mark this field as invalid
38119      * @param {String} msg The validation message
38120      */
38121     markInvalid : function(msg)
38122     {
38123         
38124         var label = this.el.select('label', true).first();
38125         var icon = this.el.select('i.fa-star', true).first();
38126
38127         if(label && !icon){
38128             this.el.select('.roo-date-split-field-label', true).createChild({
38129                 tag : 'i',
38130                 cls : 'text-danger fa fa-lg fa-star',
38131                 tooltip : 'This field is required',
38132                 style : 'margin-right:5px;'
38133             }, label, true);
38134         }
38135         
38136         this.fireEvent('invalid', this, msg);
38137     },
38138     
38139     clearInvalid : function()
38140     {
38141         var label = this.el.select('label', true).first();
38142         var icon = this.el.select('i.fa-star', true).first();
38143
38144         if(label && icon){
38145             icon.remove();
38146         }
38147         
38148         this.fireEvent('valid', this);
38149     },
38150     
38151     getName: function()
38152     {
38153         return this.name;
38154     }
38155     
38156 });
38157
38158  
38159
38160 /**
38161  * @class Roo.bootstrap.LayoutMasonry
38162  * @extends Roo.bootstrap.Component
38163  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
38164  * Bootstrap Layout Masonry class
38165  *
38166  * This is based on 
38167  * http://masonry.desandro.com
38168  *
38169  * The idea is to render all the bricks based on vertical width...
38170  *
38171  * The original code extends 'outlayer' - we might need to use that....
38172
38173  * @constructor
38174  * Create a new Element
38175  * @param {Object} config The config object
38176  */
38177
38178 Roo.bootstrap.LayoutMasonry = function(config){
38179     
38180     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
38181     
38182     this.bricks = [];
38183     
38184     Roo.bootstrap.LayoutMasonry.register(this);
38185     
38186     this.addEvents({
38187         // raw events
38188         /**
38189          * @event layout
38190          * Fire after layout the items
38191          * @param {Roo.bootstrap.LayoutMasonry} this
38192          * @param {Roo.EventObject} e
38193          */
38194         "layout" : true
38195     });
38196     
38197 };
38198
38199 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
38200     
38201     /**
38202      * @cfg {Boolean} isLayoutInstant = no animation?
38203      */   
38204     isLayoutInstant : false, // needed?
38205    
38206     /**
38207      * @cfg {Number} boxWidth  width of the columns
38208      */   
38209     boxWidth : 450,
38210     
38211       /**
38212      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
38213      */   
38214     boxHeight : 0,
38215     
38216     /**
38217      * @cfg {Number} padWidth padding below box..
38218      */   
38219     padWidth : 10, 
38220     
38221     /**
38222      * @cfg {Number} gutter gutter width..
38223      */   
38224     gutter : 10,
38225     
38226      /**
38227      * @cfg {Number} maxCols maximum number of columns
38228      */   
38229     
38230     maxCols: 0,
38231     
38232     /**
38233      * @cfg {Boolean} isAutoInitial defalut true
38234      */   
38235     isAutoInitial : true, 
38236     
38237     containerWidth: 0,
38238     
38239     /**
38240      * @cfg {Boolean} isHorizontal defalut false
38241      */   
38242     isHorizontal : false, 
38243
38244     currentSize : null,
38245     
38246     tag: 'div',
38247     
38248     cls: '',
38249     
38250     bricks: null, //CompositeElement
38251     
38252     cols : 1,
38253     
38254     _isLayoutInited : false,
38255     
38256 //    isAlternative : false, // only use for vertical layout...
38257     
38258     /**
38259      * @cfg {Number} alternativePadWidth padding below box..
38260      */   
38261     alternativePadWidth : 50,
38262     
38263     selectedBrick : [],
38264     
38265     getAutoCreate : function(){
38266         
38267         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
38268         
38269         var cfg = {
38270             tag: this.tag,
38271             cls: 'blog-masonary-wrapper ' + this.cls,
38272             cn : {
38273                 cls : 'mas-boxes masonary'
38274             }
38275         };
38276         
38277         return cfg;
38278     },
38279     
38280     getChildContainer: function( )
38281     {
38282         if (this.boxesEl) {
38283             return this.boxesEl;
38284         }
38285         
38286         this.boxesEl = this.el.select('.mas-boxes').first();
38287         
38288         return this.boxesEl;
38289     },
38290     
38291     
38292     initEvents : function()
38293     {
38294         var _this = this;
38295         
38296         if(this.isAutoInitial){
38297             Roo.log('hook children rendered');
38298             this.on('childrenrendered', function() {
38299                 Roo.log('children rendered');
38300                 _this.initial();
38301             } ,this);
38302         }
38303     },
38304     
38305     initial : function()
38306     {
38307         this.selectedBrick = [];
38308         
38309         this.currentSize = this.el.getBox(true);
38310         
38311         Roo.EventManager.onWindowResize(this.resize, this); 
38312
38313         if(!this.isAutoInitial){
38314             this.layout();
38315             return;
38316         }
38317         
38318         this.layout();
38319         
38320         return;
38321         //this.layout.defer(500,this);
38322         
38323     },
38324     
38325     resize : function()
38326     {
38327         var cs = this.el.getBox(true);
38328         
38329         if (
38330                 this.currentSize.width == cs.width && 
38331                 this.currentSize.x == cs.x && 
38332                 this.currentSize.height == cs.height && 
38333                 this.currentSize.y == cs.y 
38334         ) {
38335             Roo.log("no change in with or X or Y");
38336             return;
38337         }
38338         
38339         this.currentSize = cs;
38340         
38341         this.layout();
38342         
38343     },
38344     
38345     layout : function()
38346     {   
38347         this._resetLayout();
38348         
38349         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38350         
38351         this.layoutItems( isInstant );
38352       
38353         this._isLayoutInited = true;
38354         
38355         this.fireEvent('layout', this);
38356         
38357     },
38358     
38359     _resetLayout : function()
38360     {
38361         if(this.isHorizontal){
38362             this.horizontalMeasureColumns();
38363             return;
38364         }
38365         
38366         this.verticalMeasureColumns();
38367         
38368     },
38369     
38370     verticalMeasureColumns : function()
38371     {
38372         this.getContainerWidth();
38373         
38374 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38375 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
38376 //            return;
38377 //        }
38378         
38379         var boxWidth = this.boxWidth + this.padWidth;
38380         
38381         if(this.containerWidth < this.boxWidth){
38382             boxWidth = this.containerWidth
38383         }
38384         
38385         var containerWidth = this.containerWidth;
38386         
38387         var cols = Math.floor(containerWidth / boxWidth);
38388         
38389         this.cols = Math.max( cols, 1 );
38390         
38391         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38392         
38393         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
38394         
38395         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
38396         
38397         this.colWidth = boxWidth + avail - this.padWidth;
38398         
38399         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
38400         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
38401     },
38402     
38403     horizontalMeasureColumns : function()
38404     {
38405         this.getContainerWidth();
38406         
38407         var boxWidth = this.boxWidth;
38408         
38409         if(this.containerWidth < boxWidth){
38410             boxWidth = this.containerWidth;
38411         }
38412         
38413         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
38414         
38415         this.el.setHeight(boxWidth);
38416         
38417     },
38418     
38419     getContainerWidth : function()
38420     {
38421         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38422     },
38423     
38424     layoutItems : function( isInstant )
38425     {
38426         Roo.log(this.bricks);
38427         
38428         var items = Roo.apply([], this.bricks);
38429         
38430         if(this.isHorizontal){
38431             this._horizontalLayoutItems( items , isInstant );
38432             return;
38433         }
38434         
38435 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38436 //            this._verticalAlternativeLayoutItems( items , isInstant );
38437 //            return;
38438 //        }
38439         
38440         this._verticalLayoutItems( items , isInstant );
38441         
38442     },
38443     
38444     _verticalLayoutItems : function ( items , isInstant)
38445     {
38446         if ( !items || !items.length ) {
38447             return;
38448         }
38449         
38450         var standard = [
38451             ['xs', 'xs', 'xs', 'tall'],
38452             ['xs', 'xs', 'tall'],
38453             ['xs', 'xs', 'sm'],
38454             ['xs', 'xs', 'xs'],
38455             ['xs', 'tall'],
38456             ['xs', 'sm'],
38457             ['xs', 'xs'],
38458             ['xs'],
38459             
38460             ['sm', 'xs', 'xs'],
38461             ['sm', 'xs'],
38462             ['sm'],
38463             
38464             ['tall', 'xs', 'xs', 'xs'],
38465             ['tall', 'xs', 'xs'],
38466             ['tall', 'xs'],
38467             ['tall']
38468             
38469         ];
38470         
38471         var queue = [];
38472         
38473         var boxes = [];
38474         
38475         var box = [];
38476         
38477         Roo.each(items, function(item, k){
38478             
38479             switch (item.size) {
38480                 // these layouts take up a full box,
38481                 case 'md' :
38482                 case 'md-left' :
38483                 case 'md-right' :
38484                 case 'wide' :
38485                     
38486                     if(box.length){
38487                         boxes.push(box);
38488                         box = [];
38489                     }
38490                     
38491                     boxes.push([item]);
38492                     
38493                     break;
38494                     
38495                 case 'xs' :
38496                 case 'sm' :
38497                 case 'tall' :
38498                     
38499                     box.push(item);
38500                     
38501                     break;
38502                 default :
38503                     break;
38504                     
38505             }
38506             
38507         }, this);
38508         
38509         if(box.length){
38510             boxes.push(box);
38511             box = [];
38512         }
38513         
38514         var filterPattern = function(box, length)
38515         {
38516             if(!box.length){
38517                 return;
38518             }
38519             
38520             var match = false;
38521             
38522             var pattern = box.slice(0, length);
38523             
38524             var format = [];
38525             
38526             Roo.each(pattern, function(i){
38527                 format.push(i.size);
38528             }, this);
38529             
38530             Roo.each(standard, function(s){
38531                 
38532                 if(String(s) != String(format)){
38533                     return;
38534                 }
38535                 
38536                 match = true;
38537                 return false;
38538                 
38539             }, this);
38540             
38541             if(!match && length == 1){
38542                 return;
38543             }
38544             
38545             if(!match){
38546                 filterPattern(box, length - 1);
38547                 return;
38548             }
38549                 
38550             queue.push(pattern);
38551
38552             box = box.slice(length, box.length);
38553
38554             filterPattern(box, 4);
38555
38556             return;
38557             
38558         }
38559         
38560         Roo.each(boxes, function(box, k){
38561             
38562             if(!box.length){
38563                 return;
38564             }
38565             
38566             if(box.length == 1){
38567                 queue.push(box);
38568                 return;
38569             }
38570             
38571             filterPattern(box, 4);
38572             
38573         }, this);
38574         
38575         this._processVerticalLayoutQueue( queue, isInstant );
38576         
38577     },
38578     
38579 //    _verticalAlternativeLayoutItems : function( items , isInstant )
38580 //    {
38581 //        if ( !items || !items.length ) {
38582 //            return;
38583 //        }
38584 //
38585 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
38586 //        
38587 //    },
38588     
38589     _horizontalLayoutItems : function ( items , isInstant)
38590     {
38591         if ( !items || !items.length || items.length < 3) {
38592             return;
38593         }
38594         
38595         items.reverse();
38596         
38597         var eItems = items.slice(0, 3);
38598         
38599         items = items.slice(3, items.length);
38600         
38601         var standard = [
38602             ['xs', 'xs', 'xs', 'wide'],
38603             ['xs', 'xs', 'wide'],
38604             ['xs', 'xs', 'sm'],
38605             ['xs', 'xs', 'xs'],
38606             ['xs', 'wide'],
38607             ['xs', 'sm'],
38608             ['xs', 'xs'],
38609             ['xs'],
38610             
38611             ['sm', 'xs', 'xs'],
38612             ['sm', 'xs'],
38613             ['sm'],
38614             
38615             ['wide', 'xs', 'xs', 'xs'],
38616             ['wide', 'xs', 'xs'],
38617             ['wide', 'xs'],
38618             ['wide'],
38619             
38620             ['wide-thin']
38621         ];
38622         
38623         var queue = [];
38624         
38625         var boxes = [];
38626         
38627         var box = [];
38628         
38629         Roo.each(items, function(item, k){
38630             
38631             switch (item.size) {
38632                 case 'md' :
38633                 case 'md-left' :
38634                 case 'md-right' :
38635                 case 'tall' :
38636                     
38637                     if(box.length){
38638                         boxes.push(box);
38639                         box = [];
38640                     }
38641                     
38642                     boxes.push([item]);
38643                     
38644                     break;
38645                     
38646                 case 'xs' :
38647                 case 'sm' :
38648                 case 'wide' :
38649                 case 'wide-thin' :
38650                     
38651                     box.push(item);
38652                     
38653                     break;
38654                 default :
38655                     break;
38656                     
38657             }
38658             
38659         }, this);
38660         
38661         if(box.length){
38662             boxes.push(box);
38663             box = [];
38664         }
38665         
38666         var filterPattern = function(box, length)
38667         {
38668             if(!box.length){
38669                 return;
38670             }
38671             
38672             var match = false;
38673             
38674             var pattern = box.slice(0, length);
38675             
38676             var format = [];
38677             
38678             Roo.each(pattern, function(i){
38679                 format.push(i.size);
38680             }, this);
38681             
38682             Roo.each(standard, function(s){
38683                 
38684                 if(String(s) != String(format)){
38685                     return;
38686                 }
38687                 
38688                 match = true;
38689                 return false;
38690                 
38691             }, this);
38692             
38693             if(!match && length == 1){
38694                 return;
38695             }
38696             
38697             if(!match){
38698                 filterPattern(box, length - 1);
38699                 return;
38700             }
38701                 
38702             queue.push(pattern);
38703
38704             box = box.slice(length, box.length);
38705
38706             filterPattern(box, 4);
38707
38708             return;
38709             
38710         }
38711         
38712         Roo.each(boxes, function(box, k){
38713             
38714             if(!box.length){
38715                 return;
38716             }
38717             
38718             if(box.length == 1){
38719                 queue.push(box);
38720                 return;
38721             }
38722             
38723             filterPattern(box, 4);
38724             
38725         }, this);
38726         
38727         
38728         var prune = [];
38729         
38730         var pos = this.el.getBox(true);
38731         
38732         var minX = pos.x;
38733         
38734         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38735         
38736         var hit_end = false;
38737         
38738         Roo.each(queue, function(box){
38739             
38740             if(hit_end){
38741                 
38742                 Roo.each(box, function(b){
38743                 
38744                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
38745                     b.el.hide();
38746
38747                 }, this);
38748
38749                 return;
38750             }
38751             
38752             var mx = 0;
38753             
38754             Roo.each(box, function(b){
38755                 
38756                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
38757                 b.el.show();
38758
38759                 mx = Math.max(mx, b.x);
38760                 
38761             }, this);
38762             
38763             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
38764             
38765             if(maxX < minX){
38766                 
38767                 Roo.each(box, function(b){
38768                 
38769                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
38770                     b.el.hide();
38771                     
38772                 }, this);
38773                 
38774                 hit_end = true;
38775                 
38776                 return;
38777             }
38778             
38779             prune.push(box);
38780             
38781         }, this);
38782         
38783         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
38784     },
38785     
38786     /** Sets position of item in DOM
38787     * @param {Element} item
38788     * @param {Number} x - horizontal position
38789     * @param {Number} y - vertical position
38790     * @param {Boolean} isInstant - disables transitions
38791     */
38792     _processVerticalLayoutQueue : function( queue, isInstant )
38793     {
38794         var pos = this.el.getBox(true);
38795         var x = pos.x;
38796         var y = pos.y;
38797         var maxY = [];
38798         
38799         for (var i = 0; i < this.cols; i++){
38800             maxY[i] = pos.y;
38801         }
38802         
38803         Roo.each(queue, function(box, k){
38804             
38805             var col = k % this.cols;
38806             
38807             Roo.each(box, function(b,kk){
38808                 
38809                 b.el.position('absolute');
38810                 
38811                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38812                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
38813                 
38814                 if(b.size == 'md-left' || b.size == 'md-right'){
38815                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
38816                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
38817                 }
38818                 
38819                 b.el.setWidth(width);
38820                 b.el.setHeight(height);
38821                 // iframe?
38822                 b.el.select('iframe',true).setSize(width,height);
38823                 
38824             }, this);
38825             
38826             for (var i = 0; i < this.cols; i++){
38827                 
38828                 if(maxY[i] < maxY[col]){
38829                     col = i;
38830                     continue;
38831                 }
38832                 
38833                 col = Math.min(col, i);
38834                 
38835             }
38836             
38837             x = pos.x + col * (this.colWidth + this.padWidth);
38838             
38839             y = maxY[col];
38840             
38841             var positions = [];
38842             
38843             switch (box.length){
38844                 case 1 :
38845                     positions = this.getVerticalOneBoxColPositions(x, y, box);
38846                     break;
38847                 case 2 :
38848                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
38849                     break;
38850                 case 3 :
38851                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
38852                     break;
38853                 case 4 :
38854                     positions = this.getVerticalFourBoxColPositions(x, y, box);
38855                     break;
38856                 default :
38857                     break;
38858             }
38859             
38860             Roo.each(box, function(b,kk){
38861                 
38862                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
38863                 
38864                 var sz = b.el.getSize();
38865                 
38866                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
38867                 
38868             }, this);
38869             
38870         }, this);
38871         
38872         var mY = 0;
38873         
38874         for (var i = 0; i < this.cols; i++){
38875             mY = Math.max(mY, maxY[i]);
38876         }
38877         
38878         this.el.setHeight(mY - pos.y);
38879         
38880     },
38881     
38882 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
38883 //    {
38884 //        var pos = this.el.getBox(true);
38885 //        var x = pos.x;
38886 //        var y = pos.y;
38887 //        var maxX = pos.right;
38888 //        
38889 //        var maxHeight = 0;
38890 //        
38891 //        Roo.each(items, function(item, k){
38892 //            
38893 //            var c = k % 2;
38894 //            
38895 //            item.el.position('absolute');
38896 //                
38897 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
38898 //
38899 //            item.el.setWidth(width);
38900 //
38901 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
38902 //
38903 //            item.el.setHeight(height);
38904 //            
38905 //            if(c == 0){
38906 //                item.el.setXY([x, y], isInstant ? false : true);
38907 //            } else {
38908 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
38909 //            }
38910 //            
38911 //            y = y + height + this.alternativePadWidth;
38912 //            
38913 //            maxHeight = maxHeight + height + this.alternativePadWidth;
38914 //            
38915 //        }, this);
38916 //        
38917 //        this.el.setHeight(maxHeight);
38918 //        
38919 //    },
38920     
38921     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
38922     {
38923         var pos = this.el.getBox(true);
38924         
38925         var minX = pos.x;
38926         var minY = pos.y;
38927         
38928         var maxX = pos.right;
38929         
38930         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
38931         
38932         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38933         
38934         Roo.each(queue, function(box, k){
38935             
38936             Roo.each(box, function(b, kk){
38937                 
38938                 b.el.position('absolute');
38939                 
38940                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38941                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
38942                 
38943                 if(b.size == 'md-left' || b.size == 'md-right'){
38944                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
38945                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
38946                 }
38947                 
38948                 b.el.setWidth(width);
38949                 b.el.setHeight(height);
38950                 
38951             }, this);
38952             
38953             if(!box.length){
38954                 return;
38955             }
38956             
38957             var positions = [];
38958             
38959             switch (box.length){
38960                 case 1 :
38961                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
38962                     break;
38963                 case 2 :
38964                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
38965                     break;
38966                 case 3 :
38967                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
38968                     break;
38969                 case 4 :
38970                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
38971                     break;
38972                 default :
38973                     break;
38974             }
38975             
38976             Roo.each(box, function(b,kk){
38977                 
38978                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
38979                 
38980                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
38981                 
38982             }, this);
38983             
38984         }, this);
38985         
38986     },
38987     
38988     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
38989     {
38990         Roo.each(eItems, function(b,k){
38991             
38992             b.size = (k == 0) ? 'sm' : 'xs';
38993             b.x = (k == 0) ? 2 : 1;
38994             b.y = (k == 0) ? 2 : 1;
38995             
38996             b.el.position('absolute');
38997             
38998             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38999                 
39000             b.el.setWidth(width);
39001             
39002             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39003             
39004             b.el.setHeight(height);
39005             
39006         }, this);
39007
39008         var positions = [];
39009         
39010         positions.push({
39011             x : maxX - this.unitWidth * 2 - this.gutter,
39012             y : minY
39013         });
39014         
39015         positions.push({
39016             x : maxX - this.unitWidth,
39017             y : minY + (this.unitWidth + this.gutter) * 2
39018         });
39019         
39020         positions.push({
39021             x : maxX - this.unitWidth * 3 - this.gutter * 2,
39022             y : minY
39023         });
39024         
39025         Roo.each(eItems, function(b,k){
39026             
39027             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39028
39029         }, this);
39030         
39031     },
39032     
39033     getVerticalOneBoxColPositions : function(x, y, box)
39034     {
39035         var pos = [];
39036         
39037         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39038         
39039         if(box[0].size == 'md-left'){
39040             rand = 0;
39041         }
39042         
39043         if(box[0].size == 'md-right'){
39044             rand = 1;
39045         }
39046         
39047         pos.push({
39048             x : x + (this.unitWidth + this.gutter) * rand,
39049             y : y
39050         });
39051         
39052         return pos;
39053     },
39054     
39055     getVerticalTwoBoxColPositions : function(x, y, box)
39056     {
39057         var pos = [];
39058         
39059         if(box[0].size == 'xs'){
39060             
39061             pos.push({
39062                 x : x,
39063                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39064             });
39065
39066             pos.push({
39067                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39068                 y : y
39069             });
39070             
39071             return pos;
39072             
39073         }
39074         
39075         pos.push({
39076             x : x,
39077             y : y
39078         });
39079
39080         pos.push({
39081             x : x + (this.unitWidth + this.gutter) * 2,
39082             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39083         });
39084         
39085         return pos;
39086         
39087     },
39088     
39089     getVerticalThreeBoxColPositions : function(x, y, box)
39090     {
39091         var pos = [];
39092         
39093         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39094             
39095             pos.push({
39096                 x : x,
39097                 y : y
39098             });
39099
39100             pos.push({
39101                 x : x + (this.unitWidth + this.gutter) * 1,
39102                 y : y
39103             });
39104             
39105             pos.push({
39106                 x : x + (this.unitWidth + this.gutter) * 2,
39107                 y : y
39108             });
39109             
39110             return pos;
39111             
39112         }
39113         
39114         if(box[0].size == 'xs' && box[1].size == 'xs'){
39115             
39116             pos.push({
39117                 x : x,
39118                 y : y
39119             });
39120
39121             pos.push({
39122                 x : x,
39123                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
39124             });
39125             
39126             pos.push({
39127                 x : x + (this.unitWidth + this.gutter) * 1,
39128                 y : y
39129             });
39130             
39131             return pos;
39132             
39133         }
39134         
39135         pos.push({
39136             x : x,
39137             y : y
39138         });
39139
39140         pos.push({
39141             x : x + (this.unitWidth + this.gutter) * 2,
39142             y : y
39143         });
39144
39145         pos.push({
39146             x : x + (this.unitWidth + this.gutter) * 2,
39147             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
39148         });
39149             
39150         return pos;
39151         
39152     },
39153     
39154     getVerticalFourBoxColPositions : function(x, y, box)
39155     {
39156         var pos = [];
39157         
39158         if(box[0].size == 'xs'){
39159             
39160             pos.push({
39161                 x : x,
39162                 y : y
39163             });
39164
39165             pos.push({
39166                 x : x,
39167                 y : y + (this.unitHeight + this.gutter) * 1
39168             });
39169             
39170             pos.push({
39171                 x : x,
39172                 y : y + (this.unitHeight + this.gutter) * 2
39173             });
39174             
39175             pos.push({
39176                 x : x + (this.unitWidth + this.gutter) * 1,
39177                 y : y
39178             });
39179             
39180             return pos;
39181             
39182         }
39183         
39184         pos.push({
39185             x : x,
39186             y : y
39187         });
39188
39189         pos.push({
39190             x : x + (this.unitWidth + this.gutter) * 2,
39191             y : y
39192         });
39193
39194         pos.push({
39195             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
39196             y : y + (this.unitHeight + this.gutter) * 1
39197         });
39198
39199         pos.push({
39200             x : x + (this.unitWidth + this.gutter) * 2,
39201             y : y + (this.unitWidth + this.gutter) * 2
39202         });
39203
39204         return pos;
39205         
39206     },
39207     
39208     getHorizontalOneBoxColPositions : function(maxX, minY, box)
39209     {
39210         var pos = [];
39211         
39212         if(box[0].size == 'md-left'){
39213             pos.push({
39214                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39215                 y : minY
39216             });
39217             
39218             return pos;
39219         }
39220         
39221         if(box[0].size == 'md-right'){
39222             pos.push({
39223                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39224                 y : minY + (this.unitWidth + this.gutter) * 1
39225             });
39226             
39227             return pos;
39228         }
39229         
39230         var rand = Math.floor(Math.random() * (4 - box[0].y));
39231         
39232         pos.push({
39233             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39234             y : minY + (this.unitWidth + this.gutter) * rand
39235         });
39236         
39237         return pos;
39238         
39239     },
39240     
39241     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
39242     {
39243         var pos = [];
39244         
39245         if(box[0].size == 'xs'){
39246             
39247             pos.push({
39248                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39249                 y : minY
39250             });
39251
39252             pos.push({
39253                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39254                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
39255             });
39256             
39257             return pos;
39258             
39259         }
39260         
39261         pos.push({
39262             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39263             y : minY
39264         });
39265
39266         pos.push({
39267             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39268             y : minY + (this.unitWidth + this.gutter) * 2
39269         });
39270         
39271         return pos;
39272         
39273     },
39274     
39275     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
39276     {
39277         var pos = [];
39278         
39279         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39280             
39281             pos.push({
39282                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39283                 y : minY
39284             });
39285
39286             pos.push({
39287                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39288                 y : minY + (this.unitWidth + this.gutter) * 1
39289             });
39290             
39291             pos.push({
39292                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39293                 y : minY + (this.unitWidth + this.gutter) * 2
39294             });
39295             
39296             return pos;
39297             
39298         }
39299         
39300         if(box[0].size == 'xs' && box[1].size == 'xs'){
39301             
39302             pos.push({
39303                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39304                 y : minY
39305             });
39306
39307             pos.push({
39308                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39309                 y : minY
39310             });
39311             
39312             pos.push({
39313                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39314                 y : minY + (this.unitWidth + this.gutter) * 1
39315             });
39316             
39317             return pos;
39318             
39319         }
39320         
39321         pos.push({
39322             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39323             y : minY
39324         });
39325
39326         pos.push({
39327             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39328             y : minY + (this.unitWidth + this.gutter) * 2
39329         });
39330
39331         pos.push({
39332             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39333             y : minY + (this.unitWidth + this.gutter) * 2
39334         });
39335             
39336         return pos;
39337         
39338     },
39339     
39340     getHorizontalFourBoxColPositions : function(maxX, minY, box)
39341     {
39342         var pos = [];
39343         
39344         if(box[0].size == 'xs'){
39345             
39346             pos.push({
39347                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39348                 y : minY
39349             });
39350
39351             pos.push({
39352                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39353                 y : minY
39354             });
39355             
39356             pos.push({
39357                 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),
39358                 y : minY
39359             });
39360             
39361             pos.push({
39362                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
39363                 y : minY + (this.unitWidth + this.gutter) * 1
39364             });
39365             
39366             return pos;
39367             
39368         }
39369         
39370         pos.push({
39371             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39372             y : minY
39373         });
39374         
39375         pos.push({
39376             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39377             y : minY + (this.unitWidth + this.gutter) * 2
39378         });
39379         
39380         pos.push({
39381             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39382             y : minY + (this.unitWidth + this.gutter) * 2
39383         });
39384         
39385         pos.push({
39386             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),
39387             y : minY + (this.unitWidth + this.gutter) * 2
39388         });
39389
39390         return pos;
39391         
39392     },
39393     
39394     /**
39395     * remove a Masonry Brick
39396     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
39397     */
39398     removeBrick : function(brick_id)
39399     {
39400         if (!brick_id) {
39401             return;
39402         }
39403         
39404         for (var i = 0; i<this.bricks.length; i++) {
39405             if (this.bricks[i].id == brick_id) {
39406                 this.bricks.splice(i,1);
39407                 this.el.dom.removeChild(Roo.get(brick_id).dom);
39408                 this.initial();
39409             }
39410         }
39411     },
39412     
39413     /**
39414     * adds a Masonry Brick
39415     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39416     */
39417     addBrick : function(cfg)
39418     {
39419         var cn = new Roo.bootstrap.MasonryBrick(cfg);
39420         //this.register(cn);
39421         cn.parentId = this.id;
39422         cn.render(this.el);
39423         return cn;
39424     },
39425     
39426     /**
39427     * register a Masonry Brick
39428     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39429     */
39430     
39431     register : function(brick)
39432     {
39433         this.bricks.push(brick);
39434         brick.masonryId = this.id;
39435     },
39436     
39437     /**
39438     * clear all the Masonry Brick
39439     */
39440     clearAll : function()
39441     {
39442         this.bricks = [];
39443         //this.getChildContainer().dom.innerHTML = "";
39444         this.el.dom.innerHTML = '';
39445     },
39446     
39447     getSelected : function()
39448     {
39449         if (!this.selectedBrick) {
39450             return false;
39451         }
39452         
39453         return this.selectedBrick;
39454     }
39455 });
39456
39457 Roo.apply(Roo.bootstrap.LayoutMasonry, {
39458     
39459     groups: {},
39460      /**
39461     * register a Masonry Layout
39462     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
39463     */
39464     
39465     register : function(layout)
39466     {
39467         this.groups[layout.id] = layout;
39468     },
39469     /**
39470     * fetch a  Masonry Layout based on the masonry layout ID
39471     * @param {string} the masonry layout to add
39472     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
39473     */
39474     
39475     get: function(layout_id) {
39476         if (typeof(this.groups[layout_id]) == 'undefined') {
39477             return false;
39478         }
39479         return this.groups[layout_id] ;
39480     }
39481     
39482     
39483     
39484 });
39485
39486  
39487
39488  /**
39489  *
39490  * This is based on 
39491  * http://masonry.desandro.com
39492  *
39493  * The idea is to render all the bricks based on vertical width...
39494  *
39495  * The original code extends 'outlayer' - we might need to use that....
39496  * 
39497  */
39498
39499
39500 /**
39501  * @class Roo.bootstrap.LayoutMasonryAuto
39502  * @extends Roo.bootstrap.Component
39503  * Bootstrap Layout Masonry class
39504  * 
39505  * @constructor
39506  * Create a new Element
39507  * @param {Object} config The config object
39508  */
39509
39510 Roo.bootstrap.LayoutMasonryAuto = function(config){
39511     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
39512 };
39513
39514 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
39515     
39516       /**
39517      * @cfg {Boolean} isFitWidth  - resize the width..
39518      */   
39519     isFitWidth : false,  // options..
39520     /**
39521      * @cfg {Boolean} isOriginLeft = left align?
39522      */   
39523     isOriginLeft : true,
39524     /**
39525      * @cfg {Boolean} isOriginTop = top align?
39526      */   
39527     isOriginTop : false,
39528     /**
39529      * @cfg {Boolean} isLayoutInstant = no animation?
39530      */   
39531     isLayoutInstant : false, // needed?
39532     /**
39533      * @cfg {Boolean} isResizingContainer = not sure if this is used..
39534      */   
39535     isResizingContainer : true,
39536     /**
39537      * @cfg {Number} columnWidth  width of the columns 
39538      */   
39539     
39540     columnWidth : 0,
39541     
39542     /**
39543      * @cfg {Number} maxCols maximum number of columns
39544      */   
39545     
39546     maxCols: 0,
39547     /**
39548      * @cfg {Number} padHeight padding below box..
39549      */   
39550     
39551     padHeight : 10, 
39552     
39553     /**
39554      * @cfg {Boolean} isAutoInitial defalut true
39555      */   
39556     
39557     isAutoInitial : true, 
39558     
39559     // private?
39560     gutter : 0,
39561     
39562     containerWidth: 0,
39563     initialColumnWidth : 0,
39564     currentSize : null,
39565     
39566     colYs : null, // array.
39567     maxY : 0,
39568     padWidth: 10,
39569     
39570     
39571     tag: 'div',
39572     cls: '',
39573     bricks: null, //CompositeElement
39574     cols : 0, // array?
39575     // element : null, // wrapped now this.el
39576     _isLayoutInited : null, 
39577     
39578     
39579     getAutoCreate : function(){
39580         
39581         var cfg = {
39582             tag: this.tag,
39583             cls: 'blog-masonary-wrapper ' + this.cls,
39584             cn : {
39585                 cls : 'mas-boxes masonary'
39586             }
39587         };
39588         
39589         return cfg;
39590     },
39591     
39592     getChildContainer: function( )
39593     {
39594         if (this.boxesEl) {
39595             return this.boxesEl;
39596         }
39597         
39598         this.boxesEl = this.el.select('.mas-boxes').first();
39599         
39600         return this.boxesEl;
39601     },
39602     
39603     
39604     initEvents : function()
39605     {
39606         var _this = this;
39607         
39608         if(this.isAutoInitial){
39609             Roo.log('hook children rendered');
39610             this.on('childrenrendered', function() {
39611                 Roo.log('children rendered');
39612                 _this.initial();
39613             } ,this);
39614         }
39615         
39616     },
39617     
39618     initial : function()
39619     {
39620         this.reloadItems();
39621
39622         this.currentSize = this.el.getBox(true);
39623
39624         /// was window resize... - let's see if this works..
39625         Roo.EventManager.onWindowResize(this.resize, this); 
39626
39627         if(!this.isAutoInitial){
39628             this.layout();
39629             return;
39630         }
39631         
39632         this.layout.defer(500,this);
39633     },
39634     
39635     reloadItems: function()
39636     {
39637         this.bricks = this.el.select('.masonry-brick', true);
39638         
39639         this.bricks.each(function(b) {
39640             //Roo.log(b.getSize());
39641             if (!b.attr('originalwidth')) {
39642                 b.attr('originalwidth',  b.getSize().width);
39643             }
39644             
39645         });
39646         
39647         Roo.log(this.bricks.elements.length);
39648     },
39649     
39650     resize : function()
39651     {
39652         Roo.log('resize');
39653         var cs = this.el.getBox(true);
39654         
39655         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
39656             Roo.log("no change in with or X");
39657             return;
39658         }
39659         this.currentSize = cs;
39660         this.layout();
39661     },
39662     
39663     layout : function()
39664     {
39665          Roo.log('layout');
39666         this._resetLayout();
39667         //this._manageStamps();
39668       
39669         // don't animate first layout
39670         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
39671         this.layoutItems( isInstant );
39672       
39673         // flag for initalized
39674         this._isLayoutInited = true;
39675     },
39676     
39677     layoutItems : function( isInstant )
39678     {
39679         //var items = this._getItemsForLayout( this.items );
39680         // original code supports filtering layout items.. we just ignore it..
39681         
39682         this._layoutItems( this.bricks , isInstant );
39683       
39684         this._postLayout();
39685     },
39686     _layoutItems : function ( items , isInstant)
39687     {
39688        //this.fireEvent( 'layout', this, items );
39689     
39690
39691         if ( !items || !items.elements.length ) {
39692           // no items, emit event with empty array
39693             return;
39694         }
39695
39696         var queue = [];
39697         items.each(function(item) {
39698             Roo.log("layout item");
39699             Roo.log(item);
39700             // get x/y object from method
39701             var position = this._getItemLayoutPosition( item );
39702             // enqueue
39703             position.item = item;
39704             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
39705             queue.push( position );
39706         }, this);
39707       
39708         this._processLayoutQueue( queue );
39709     },
39710     /** Sets position of item in DOM
39711     * @param {Element} item
39712     * @param {Number} x - horizontal position
39713     * @param {Number} y - vertical position
39714     * @param {Boolean} isInstant - disables transitions
39715     */
39716     _processLayoutQueue : function( queue )
39717     {
39718         for ( var i=0, len = queue.length; i < len; i++ ) {
39719             var obj = queue[i];
39720             obj.item.position('absolute');
39721             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
39722         }
39723     },
39724       
39725     
39726     /**
39727     * Any logic you want to do after each layout,
39728     * i.e. size the container
39729     */
39730     _postLayout : function()
39731     {
39732         this.resizeContainer();
39733     },
39734     
39735     resizeContainer : function()
39736     {
39737         if ( !this.isResizingContainer ) {
39738             return;
39739         }
39740         var size = this._getContainerSize();
39741         if ( size ) {
39742             this.el.setSize(size.width,size.height);
39743             this.boxesEl.setSize(size.width,size.height);
39744         }
39745     },
39746     
39747     
39748     
39749     _resetLayout : function()
39750     {
39751         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
39752         this.colWidth = this.el.getWidth();
39753         //this.gutter = this.el.getWidth(); 
39754         
39755         this.measureColumns();
39756
39757         // reset column Y
39758         var i = this.cols;
39759         this.colYs = [];
39760         while (i--) {
39761             this.colYs.push( 0 );
39762         }
39763     
39764         this.maxY = 0;
39765     },
39766
39767     measureColumns : function()
39768     {
39769         this.getContainerWidth();
39770       // if columnWidth is 0, default to outerWidth of first item
39771         if ( !this.columnWidth ) {
39772             var firstItem = this.bricks.first();
39773             Roo.log(firstItem);
39774             this.columnWidth  = this.containerWidth;
39775             if (firstItem && firstItem.attr('originalwidth') ) {
39776                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
39777             }
39778             // columnWidth fall back to item of first element
39779             Roo.log("set column width?");
39780                         this.initialColumnWidth = this.columnWidth  ;
39781
39782             // if first elem has no width, default to size of container
39783             
39784         }
39785         
39786         
39787         if (this.initialColumnWidth) {
39788             this.columnWidth = this.initialColumnWidth;
39789         }
39790         
39791         
39792             
39793         // column width is fixed at the top - however if container width get's smaller we should
39794         // reduce it...
39795         
39796         // this bit calcs how man columns..
39797             
39798         var columnWidth = this.columnWidth += this.gutter;
39799       
39800         // calculate columns
39801         var containerWidth = this.containerWidth + this.gutter;
39802         
39803         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
39804         // fix rounding errors, typically with gutters
39805         var excess = columnWidth - containerWidth % columnWidth;
39806         
39807         
39808         // if overshoot is less than a pixel, round up, otherwise floor it
39809         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
39810         cols = Math[ mathMethod ]( cols );
39811         this.cols = Math.max( cols, 1 );
39812         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39813         
39814          // padding positioning..
39815         var totalColWidth = this.cols * this.columnWidth;
39816         var padavail = this.containerWidth - totalColWidth;
39817         // so for 2 columns - we need 3 'pads'
39818         
39819         var padNeeded = (1+this.cols) * this.padWidth;
39820         
39821         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
39822         
39823         this.columnWidth += padExtra
39824         //this.padWidth = Math.floor(padavail /  ( this.cols));
39825         
39826         // adjust colum width so that padding is fixed??
39827         
39828         // we have 3 columns ... total = width * 3
39829         // we have X left over... that should be used by 
39830         
39831         //if (this.expandC) {
39832             
39833         //}
39834         
39835         
39836         
39837     },
39838     
39839     getContainerWidth : function()
39840     {
39841        /* // container is parent if fit width
39842         var container = this.isFitWidth ? this.element.parentNode : this.element;
39843         // check that this.size and size are there
39844         // IE8 triggers resize on body size change, so they might not be
39845         
39846         var size = getSize( container );  //FIXME
39847         this.containerWidth = size && size.innerWidth; //FIXME
39848         */
39849          
39850         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
39851         
39852     },
39853     
39854     _getItemLayoutPosition : function( item )  // what is item?
39855     {
39856         // we resize the item to our columnWidth..
39857       
39858         item.setWidth(this.columnWidth);
39859         item.autoBoxAdjust  = false;
39860         
39861         var sz = item.getSize();
39862  
39863         // how many columns does this brick span
39864         var remainder = this.containerWidth % this.columnWidth;
39865         
39866         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
39867         // round if off by 1 pixel, otherwise use ceil
39868         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
39869         colSpan = Math.min( colSpan, this.cols );
39870         
39871         // normally this should be '1' as we dont' currently allow multi width columns..
39872         
39873         var colGroup = this._getColGroup( colSpan );
39874         // get the minimum Y value from the columns
39875         var minimumY = Math.min.apply( Math, colGroup );
39876         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
39877         
39878         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
39879          
39880         // position the brick
39881         var position = {
39882             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
39883             y: this.currentSize.y + minimumY + this.padHeight
39884         };
39885         
39886         Roo.log(position);
39887         // apply setHeight to necessary columns
39888         var setHeight = minimumY + sz.height + this.padHeight;
39889         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
39890         
39891         var setSpan = this.cols + 1 - colGroup.length;
39892         for ( var i = 0; i < setSpan; i++ ) {
39893           this.colYs[ shortColIndex + i ] = setHeight ;
39894         }
39895       
39896         return position;
39897     },
39898     
39899     /**
39900      * @param {Number} colSpan - number of columns the element spans
39901      * @returns {Array} colGroup
39902      */
39903     _getColGroup : function( colSpan )
39904     {
39905         if ( colSpan < 2 ) {
39906           // if brick spans only one column, use all the column Ys
39907           return this.colYs;
39908         }
39909       
39910         var colGroup = [];
39911         // how many different places could this brick fit horizontally
39912         var groupCount = this.cols + 1 - colSpan;
39913         // for each group potential horizontal position
39914         for ( var i = 0; i < groupCount; i++ ) {
39915           // make an array of colY values for that one group
39916           var groupColYs = this.colYs.slice( i, i + colSpan );
39917           // and get the max value of the array
39918           colGroup[i] = Math.max.apply( Math, groupColYs );
39919         }
39920         return colGroup;
39921     },
39922     /*
39923     _manageStamp : function( stamp )
39924     {
39925         var stampSize =  stamp.getSize();
39926         var offset = stamp.getBox();
39927         // get the columns that this stamp affects
39928         var firstX = this.isOriginLeft ? offset.x : offset.right;
39929         var lastX = firstX + stampSize.width;
39930         var firstCol = Math.floor( firstX / this.columnWidth );
39931         firstCol = Math.max( 0, firstCol );
39932         
39933         var lastCol = Math.floor( lastX / this.columnWidth );
39934         // lastCol should not go over if multiple of columnWidth #425
39935         lastCol -= lastX % this.columnWidth ? 0 : 1;
39936         lastCol = Math.min( this.cols - 1, lastCol );
39937         
39938         // set colYs to bottom of the stamp
39939         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
39940             stampSize.height;
39941             
39942         for ( var i = firstCol; i <= lastCol; i++ ) {
39943           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
39944         }
39945     },
39946     */
39947     
39948     _getContainerSize : function()
39949     {
39950         this.maxY = Math.max.apply( Math, this.colYs );
39951         var size = {
39952             height: this.maxY
39953         };
39954       
39955         if ( this.isFitWidth ) {
39956             size.width = this._getContainerFitWidth();
39957         }
39958       
39959         return size;
39960     },
39961     
39962     _getContainerFitWidth : function()
39963     {
39964         var unusedCols = 0;
39965         // count unused columns
39966         var i = this.cols;
39967         while ( --i ) {
39968           if ( this.colYs[i] !== 0 ) {
39969             break;
39970           }
39971           unusedCols++;
39972         }
39973         // fit container to columns that have been used
39974         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
39975     },
39976     
39977     needsResizeLayout : function()
39978     {
39979         var previousWidth = this.containerWidth;
39980         this.getContainerWidth();
39981         return previousWidth !== this.containerWidth;
39982     }
39983  
39984 });
39985
39986  
39987
39988  /*
39989  * - LGPL
39990  *
39991  * element
39992  * 
39993  */
39994
39995 /**
39996  * @class Roo.bootstrap.MasonryBrick
39997  * @extends Roo.bootstrap.Component
39998  * Bootstrap MasonryBrick class
39999  * 
40000  * @constructor
40001  * Create a new MasonryBrick
40002  * @param {Object} config The config object
40003  */
40004
40005 Roo.bootstrap.MasonryBrick = function(config){
40006     
40007     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40008     
40009     Roo.bootstrap.MasonryBrick.register(this);
40010     
40011     this.addEvents({
40012         // raw events
40013         /**
40014          * @event click
40015          * When a MasonryBrick is clcik
40016          * @param {Roo.bootstrap.MasonryBrick} this
40017          * @param {Roo.EventObject} e
40018          */
40019         "click" : true
40020     });
40021 };
40022
40023 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
40024     
40025     /**
40026      * @cfg {String} title
40027      */   
40028     title : '',
40029     /**
40030      * @cfg {String} html
40031      */   
40032     html : '',
40033     /**
40034      * @cfg {String} bgimage
40035      */   
40036     bgimage : '',
40037     /**
40038      * @cfg {String} videourl
40039      */   
40040     videourl : '',
40041     /**
40042      * @cfg {String} cls
40043      */   
40044     cls : '',
40045     /**
40046      * @cfg {String} href
40047      */   
40048     href : '',
40049     /**
40050      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40051      */   
40052     size : 'xs',
40053     
40054     /**
40055      * @cfg {String} placetitle (center|bottom)
40056      */   
40057     placetitle : '',
40058     
40059     /**
40060      * @cfg {Boolean} isFitContainer defalut true
40061      */   
40062     isFitContainer : true, 
40063     
40064     /**
40065      * @cfg {Boolean} preventDefault defalut false
40066      */   
40067     preventDefault : false, 
40068     
40069     /**
40070      * @cfg {Boolean} inverse defalut false
40071      */   
40072     maskInverse : false, 
40073     
40074     getAutoCreate : function()
40075     {
40076         if(!this.isFitContainer){
40077             return this.getSplitAutoCreate();
40078         }
40079         
40080         var cls = 'masonry-brick masonry-brick-full';
40081         
40082         if(this.href.length){
40083             cls += ' masonry-brick-link';
40084         }
40085         
40086         if(this.bgimage.length){
40087             cls += ' masonry-brick-image';
40088         }
40089         
40090         if(this.maskInverse){
40091             cls += ' mask-inverse';
40092         }
40093         
40094         if(!this.html.length && !this.maskInverse && !this.videourl.length){
40095             cls += ' enable-mask';
40096         }
40097         
40098         if(this.size){
40099             cls += ' masonry-' + this.size + '-brick';
40100         }
40101         
40102         if(this.placetitle.length){
40103             
40104             switch (this.placetitle) {
40105                 case 'center' :
40106                     cls += ' masonry-center-title';
40107                     break;
40108                 case 'bottom' :
40109                     cls += ' masonry-bottom-title';
40110                     break;
40111                 default:
40112                     break;
40113             }
40114             
40115         } else {
40116             if(!this.html.length && !this.bgimage.length){
40117                 cls += ' masonry-center-title';
40118             }
40119
40120             if(!this.html.length && this.bgimage.length){
40121                 cls += ' masonry-bottom-title';
40122             }
40123         }
40124         
40125         if(this.cls){
40126             cls += ' ' + this.cls;
40127         }
40128         
40129         var cfg = {
40130             tag: (this.href.length) ? 'a' : 'div',
40131             cls: cls,
40132             cn: [
40133                 {
40134                     tag: 'div',
40135                     cls: 'masonry-brick-mask'
40136                 },
40137                 {
40138                     tag: 'div',
40139                     cls: 'masonry-brick-paragraph',
40140                     cn: []
40141                 }
40142             ]
40143         };
40144         
40145         if(this.href.length){
40146             cfg.href = this.href;
40147         }
40148         
40149         var cn = cfg.cn[1].cn;
40150         
40151         if(this.title.length){
40152             cn.push({
40153                 tag: 'h4',
40154                 cls: 'masonry-brick-title',
40155                 html: this.title
40156             });
40157         }
40158         
40159         if(this.html.length){
40160             cn.push({
40161                 tag: 'p',
40162                 cls: 'masonry-brick-text',
40163                 html: this.html
40164             });
40165         }
40166         
40167         if (!this.title.length && !this.html.length) {
40168             cfg.cn[1].cls += ' hide';
40169         }
40170         
40171         if(this.bgimage.length){
40172             cfg.cn.push({
40173                 tag: 'img',
40174                 cls: 'masonry-brick-image-view',
40175                 src: this.bgimage
40176             });
40177         }
40178         
40179         if(this.videourl.length){
40180             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40181             // youtube support only?
40182             cfg.cn.push({
40183                 tag: 'iframe',
40184                 cls: 'masonry-brick-image-view',
40185                 src: vurl,
40186                 frameborder : 0,
40187                 allowfullscreen : true
40188             });
40189         }
40190         
40191         return cfg;
40192         
40193     },
40194     
40195     getSplitAutoCreate : function()
40196     {
40197         var cls = 'masonry-brick masonry-brick-split';
40198         
40199         if(this.href.length){
40200             cls += ' masonry-brick-link';
40201         }
40202         
40203         if(this.bgimage.length){
40204             cls += ' masonry-brick-image';
40205         }
40206         
40207         if(this.size){
40208             cls += ' masonry-' + this.size + '-brick';
40209         }
40210         
40211         switch (this.placetitle) {
40212             case 'center' :
40213                 cls += ' masonry-center-title';
40214                 break;
40215             case 'bottom' :
40216                 cls += ' masonry-bottom-title';
40217                 break;
40218             default:
40219                 if(!this.bgimage.length){
40220                     cls += ' masonry-center-title';
40221                 }
40222
40223                 if(this.bgimage.length){
40224                     cls += ' masonry-bottom-title';
40225                 }
40226                 break;
40227         }
40228         
40229         if(this.cls){
40230             cls += ' ' + this.cls;
40231         }
40232         
40233         var cfg = {
40234             tag: (this.href.length) ? 'a' : 'div',
40235             cls: cls,
40236             cn: [
40237                 {
40238                     tag: 'div',
40239                     cls: 'masonry-brick-split-head',
40240                     cn: [
40241                         {
40242                             tag: 'div',
40243                             cls: 'masonry-brick-paragraph',
40244                             cn: []
40245                         }
40246                     ]
40247                 },
40248                 {
40249                     tag: 'div',
40250                     cls: 'masonry-brick-split-body',
40251                     cn: []
40252                 }
40253             ]
40254         };
40255         
40256         if(this.href.length){
40257             cfg.href = this.href;
40258         }
40259         
40260         if(this.title.length){
40261             cfg.cn[0].cn[0].cn.push({
40262                 tag: 'h4',
40263                 cls: 'masonry-brick-title',
40264                 html: this.title
40265             });
40266         }
40267         
40268         if(this.html.length){
40269             cfg.cn[1].cn.push({
40270                 tag: 'p',
40271                 cls: 'masonry-brick-text',
40272                 html: this.html
40273             });
40274         }
40275
40276         if(this.bgimage.length){
40277             cfg.cn[0].cn.push({
40278                 tag: 'img',
40279                 cls: 'masonry-brick-image-view',
40280                 src: this.bgimage
40281             });
40282         }
40283         
40284         if(this.videourl.length){
40285             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40286             // youtube support only?
40287             cfg.cn[0].cn.cn.push({
40288                 tag: 'iframe',
40289                 cls: 'masonry-brick-image-view',
40290                 src: vurl,
40291                 frameborder : 0,
40292                 allowfullscreen : true
40293             });
40294         }
40295         
40296         return cfg;
40297     },
40298     
40299     initEvents: function() 
40300     {
40301         switch (this.size) {
40302             case 'xs' :
40303                 this.x = 1;
40304                 this.y = 1;
40305                 break;
40306             case 'sm' :
40307                 this.x = 2;
40308                 this.y = 2;
40309                 break;
40310             case 'md' :
40311             case 'md-left' :
40312             case 'md-right' :
40313                 this.x = 3;
40314                 this.y = 3;
40315                 break;
40316             case 'tall' :
40317                 this.x = 2;
40318                 this.y = 3;
40319                 break;
40320             case 'wide' :
40321                 this.x = 3;
40322                 this.y = 2;
40323                 break;
40324             case 'wide-thin' :
40325                 this.x = 3;
40326                 this.y = 1;
40327                 break;
40328                         
40329             default :
40330                 break;
40331         }
40332         
40333         if(Roo.isTouch){
40334             this.el.on('touchstart', this.onTouchStart, this);
40335             this.el.on('touchmove', this.onTouchMove, this);
40336             this.el.on('touchend', this.onTouchEnd, this);
40337             this.el.on('contextmenu', this.onContextMenu, this);
40338         } else {
40339             this.el.on('mouseenter'  ,this.enter, this);
40340             this.el.on('mouseleave', this.leave, this);
40341             this.el.on('click', this.onClick, this);
40342         }
40343         
40344         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
40345             this.parent().bricks.push(this);   
40346         }
40347         
40348     },
40349     
40350     onClick: function(e, el)
40351     {
40352         var time = this.endTimer - this.startTimer;
40353         // Roo.log(e.preventDefault());
40354         if(Roo.isTouch){
40355             if(time > 1000){
40356                 e.preventDefault();
40357                 return;
40358             }
40359         }
40360         
40361         if(!this.preventDefault){
40362             return;
40363         }
40364         
40365         e.preventDefault();
40366         
40367         if (this.activeClass != '') {
40368             this.selectBrick();
40369         }
40370         
40371         this.fireEvent('click', this, e);
40372     },
40373     
40374     enter: function(e, el)
40375     {
40376         e.preventDefault();
40377         
40378         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40379             return;
40380         }
40381         
40382         if(this.bgimage.length && this.html.length){
40383             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40384         }
40385     },
40386     
40387     leave: function(e, el)
40388     {
40389         e.preventDefault();
40390         
40391         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
40392             return;
40393         }
40394         
40395         if(this.bgimage.length && this.html.length){
40396             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40397         }
40398     },
40399     
40400     onTouchStart: function(e, el)
40401     {
40402 //        e.preventDefault();
40403         
40404         this.touchmoved = false;
40405         
40406         if(!this.isFitContainer){
40407             return;
40408         }
40409         
40410         if(!this.bgimage.length || !this.html.length){
40411             return;
40412         }
40413         
40414         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40415         
40416         this.timer = new Date().getTime();
40417         
40418     },
40419     
40420     onTouchMove: function(e, el)
40421     {
40422         this.touchmoved = true;
40423     },
40424     
40425     onContextMenu : function(e,el)
40426     {
40427         e.preventDefault();
40428         e.stopPropagation();
40429         return false;
40430     },
40431     
40432     onTouchEnd: function(e, el)
40433     {
40434 //        e.preventDefault();
40435         
40436         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
40437         
40438             this.leave(e,el);
40439             
40440             return;
40441         }
40442         
40443         if(!this.bgimage.length || !this.html.length){
40444             
40445             if(this.href.length){
40446                 window.location.href = this.href;
40447             }
40448             
40449             return;
40450         }
40451         
40452         if(!this.isFitContainer){
40453             return;
40454         }
40455         
40456         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40457         
40458         window.location.href = this.href;
40459     },
40460     
40461     //selection on single brick only
40462     selectBrick : function() {
40463         
40464         if (!this.parentId) {
40465             return;
40466         }
40467         
40468         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
40469         var index = m.selectedBrick.indexOf(this.id);
40470         
40471         if ( index > -1) {
40472             m.selectedBrick.splice(index,1);
40473             this.el.removeClass(this.activeClass);
40474             return;
40475         }
40476         
40477         for(var i = 0; i < m.selectedBrick.length; i++) {
40478             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
40479             b.el.removeClass(b.activeClass);
40480         }
40481         
40482         m.selectedBrick = [];
40483         
40484         m.selectedBrick.push(this.id);
40485         this.el.addClass(this.activeClass);
40486         return;
40487     },
40488     
40489     isSelected : function(){
40490         return this.el.hasClass(this.activeClass);
40491         
40492     }
40493 });
40494
40495 Roo.apply(Roo.bootstrap.MasonryBrick, {
40496     
40497     //groups: {},
40498     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
40499      /**
40500     * register a Masonry Brick
40501     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40502     */
40503     
40504     register : function(brick)
40505     {
40506         //this.groups[brick.id] = brick;
40507         this.groups.add(brick.id, brick);
40508     },
40509     /**
40510     * fetch a  masonry brick based on the masonry brick ID
40511     * @param {string} the masonry brick to add
40512     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
40513     */
40514     
40515     get: function(brick_id) 
40516     {
40517         // if (typeof(this.groups[brick_id]) == 'undefined') {
40518         //     return false;
40519         // }
40520         // return this.groups[brick_id] ;
40521         
40522         if(this.groups.key(brick_id)) {
40523             return this.groups.key(brick_id);
40524         }
40525         
40526         return false;
40527     }
40528     
40529     
40530     
40531 });
40532
40533  /*
40534  * - LGPL
40535  *
40536  * element
40537  * 
40538  */
40539
40540 /**
40541  * @class Roo.bootstrap.Brick
40542  * @extends Roo.bootstrap.Component
40543  * Bootstrap Brick class
40544  * 
40545  * @constructor
40546  * Create a new Brick
40547  * @param {Object} config The config object
40548  */
40549
40550 Roo.bootstrap.Brick = function(config){
40551     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
40552     
40553     this.addEvents({
40554         // raw events
40555         /**
40556          * @event click
40557          * When a Brick is click
40558          * @param {Roo.bootstrap.Brick} this
40559          * @param {Roo.EventObject} e
40560          */
40561         "click" : true
40562     });
40563 };
40564
40565 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
40566     
40567     /**
40568      * @cfg {String} title
40569      */   
40570     title : '',
40571     /**
40572      * @cfg {String} html
40573      */   
40574     html : '',
40575     /**
40576      * @cfg {String} bgimage
40577      */   
40578     bgimage : '',
40579     /**
40580      * @cfg {String} cls
40581      */   
40582     cls : '',
40583     /**
40584      * @cfg {String} href
40585      */   
40586     href : '',
40587     /**
40588      * @cfg {String} video
40589      */   
40590     video : '',
40591     /**
40592      * @cfg {Boolean} square
40593      */   
40594     square : true,
40595     
40596     getAutoCreate : function()
40597     {
40598         var cls = 'roo-brick';
40599         
40600         if(this.href.length){
40601             cls += ' roo-brick-link';
40602         }
40603         
40604         if(this.bgimage.length){
40605             cls += ' roo-brick-image';
40606         }
40607         
40608         if(!this.html.length && !this.bgimage.length){
40609             cls += ' roo-brick-center-title';
40610         }
40611         
40612         if(!this.html.length && this.bgimage.length){
40613             cls += ' roo-brick-bottom-title';
40614         }
40615         
40616         if(this.cls){
40617             cls += ' ' + this.cls;
40618         }
40619         
40620         var cfg = {
40621             tag: (this.href.length) ? 'a' : 'div',
40622             cls: cls,
40623             cn: [
40624                 {
40625                     tag: 'div',
40626                     cls: 'roo-brick-paragraph',
40627                     cn: []
40628                 }
40629             ]
40630         };
40631         
40632         if(this.href.length){
40633             cfg.href = this.href;
40634         }
40635         
40636         var cn = cfg.cn[0].cn;
40637         
40638         if(this.title.length){
40639             cn.push({
40640                 tag: 'h4',
40641                 cls: 'roo-brick-title',
40642                 html: this.title
40643             });
40644         }
40645         
40646         if(this.html.length){
40647             cn.push({
40648                 tag: 'p',
40649                 cls: 'roo-brick-text',
40650                 html: this.html
40651             });
40652         } else {
40653             cn.cls += ' hide';
40654         }
40655         
40656         if(this.bgimage.length){
40657             cfg.cn.push({
40658                 tag: 'img',
40659                 cls: 'roo-brick-image-view',
40660                 src: this.bgimage
40661             });
40662         }
40663         
40664         return cfg;
40665     },
40666     
40667     initEvents: function() 
40668     {
40669         if(this.title.length || this.html.length){
40670             this.el.on('mouseenter'  ,this.enter, this);
40671             this.el.on('mouseleave', this.leave, this);
40672         }
40673         
40674         Roo.EventManager.onWindowResize(this.resize, this); 
40675         
40676         if(this.bgimage.length){
40677             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
40678             this.imageEl.on('load', this.onImageLoad, this);
40679             return;
40680         }
40681         
40682         this.resize();
40683     },
40684     
40685     onImageLoad : function()
40686     {
40687         this.resize();
40688     },
40689     
40690     resize : function()
40691     {
40692         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
40693         
40694         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
40695         
40696         if(this.bgimage.length){
40697             var image = this.el.select('.roo-brick-image-view', true).first();
40698             
40699             image.setWidth(paragraph.getWidth());
40700             
40701             if(this.square){
40702                 image.setHeight(paragraph.getWidth());
40703             }
40704             
40705             this.el.setHeight(image.getHeight());
40706             paragraph.setHeight(image.getHeight());
40707             
40708         }
40709         
40710     },
40711     
40712     enter: function(e, el)
40713     {
40714         e.preventDefault();
40715         
40716         if(this.bgimage.length){
40717             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
40718             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
40719         }
40720     },
40721     
40722     leave: function(e, el)
40723     {
40724         e.preventDefault();
40725         
40726         if(this.bgimage.length){
40727             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
40728             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
40729         }
40730     }
40731     
40732 });
40733
40734  
40735
40736  /*
40737  * - LGPL
40738  *
40739  * Number field 
40740  */
40741
40742 /**
40743  * @class Roo.bootstrap.form.NumberField
40744  * @extends Roo.bootstrap.form.Input
40745  * Bootstrap NumberField class
40746  * 
40747  * 
40748  * 
40749  * 
40750  * @constructor
40751  * Create a new NumberField
40752  * @param {Object} config The config object
40753  */
40754
40755 Roo.bootstrap.form.NumberField = function(config){
40756     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
40757 };
40758
40759 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
40760     
40761     /**
40762      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40763      */
40764     allowDecimals : true,
40765     /**
40766      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40767      */
40768     decimalSeparator : ".",
40769     /**
40770      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40771      */
40772     decimalPrecision : 2,
40773     /**
40774      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40775      */
40776     allowNegative : true,
40777     
40778     /**
40779      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40780      */
40781     allowZero: true,
40782     /**
40783      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40784      */
40785     minValue : Number.NEGATIVE_INFINITY,
40786     /**
40787      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40788      */
40789     maxValue : Number.MAX_VALUE,
40790     /**
40791      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40792      */
40793     minText : "The minimum value for this field is {0}",
40794     /**
40795      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40796      */
40797     maxText : "The maximum value for this field is {0}",
40798     /**
40799      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40800      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40801      */
40802     nanText : "{0} is not a valid number",
40803     /**
40804      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40805      */
40806     thousandsDelimiter : false,
40807     /**
40808      * @cfg {String} valueAlign alignment of value
40809      */
40810     valueAlign : "left",
40811
40812     getAutoCreate : function()
40813     {
40814         var hiddenInput = {
40815             tag: 'input',
40816             type: 'hidden',
40817             id: Roo.id(),
40818             cls: 'hidden-number-input'
40819         };
40820         
40821         if (this.name) {
40822             hiddenInput.name = this.name;
40823         }
40824         
40825         this.name = '';
40826         
40827         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
40828         
40829         this.name = hiddenInput.name;
40830         
40831         if(cfg.cn.length > 0) {
40832             cfg.cn.push(hiddenInput);
40833         }
40834         
40835         return cfg;
40836     },
40837
40838     // private
40839     initEvents : function()
40840     {   
40841         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
40842         
40843         var allowed = "0123456789";
40844         
40845         if(this.allowDecimals){
40846             allowed += this.decimalSeparator;
40847         }
40848         
40849         if(this.allowNegative){
40850             allowed += "-";
40851         }
40852         
40853         if(this.thousandsDelimiter) {
40854             allowed += ",";
40855         }
40856         
40857         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40858         
40859         var keyPress = function(e){
40860             
40861             var k = e.getKey();
40862             
40863             var c = e.getCharCode();
40864             
40865             if(
40866                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40867                     allowed.indexOf(String.fromCharCode(c)) === -1
40868             ){
40869                 e.stopEvent();
40870                 return;
40871             }
40872             
40873             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40874                 return;
40875             }
40876             
40877             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40878                 e.stopEvent();
40879             }
40880         };
40881         
40882         this.el.on("keypress", keyPress, this);
40883     },
40884     
40885     validateValue : function(value)
40886     {
40887         
40888         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
40889             return false;
40890         }
40891         
40892         var num = this.parseValue(value);
40893         
40894         if(isNaN(num)){
40895             this.markInvalid(String.format(this.nanText, value));
40896             return false;
40897         }
40898         
40899         if(num < this.minValue){
40900             this.markInvalid(String.format(this.minText, this.minValue));
40901             return false;
40902         }
40903         
40904         if(num > this.maxValue){
40905             this.markInvalid(String.format(this.maxText, this.maxValue));
40906             return false;
40907         }
40908         
40909         return true;
40910     },
40911
40912     getValue : function()
40913     {
40914         var v = this.hiddenEl().getValue();
40915         
40916         return this.fixPrecision(this.parseValue(v));
40917     },
40918
40919     parseValue : function(value)
40920     {
40921         if(this.thousandsDelimiter) {
40922             value += "";
40923             r = new RegExp(",", "g");
40924             value = value.replace(r, "");
40925         }
40926         
40927         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40928         return isNaN(value) ? '' : value;
40929     },
40930
40931     fixPrecision : function(value)
40932     {
40933         if(this.thousandsDelimiter) {
40934             value += "";
40935             r = new RegExp(",", "g");
40936             value = value.replace(r, "");
40937         }
40938         
40939         var nan = isNaN(value);
40940         
40941         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40942             return nan ? '' : value;
40943         }
40944         return parseFloat(value).toFixed(this.decimalPrecision);
40945     },
40946
40947     setValue : function(v)
40948     {
40949         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40950         
40951         this.value = v;
40952         
40953         if(this.rendered){
40954             
40955             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40956             
40957             this.inputEl().dom.value = (v == '') ? '' :
40958                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40959             
40960             if(!this.allowZero && v === '0') {
40961                 this.hiddenEl().dom.value = '';
40962                 this.inputEl().dom.value = '';
40963             }
40964             
40965             this.validate();
40966         }
40967     },
40968
40969     decimalPrecisionFcn : function(v)
40970     {
40971         return Math.floor(v);
40972     },
40973
40974     beforeBlur : function()
40975     {
40976         var v = this.parseValue(this.getRawValue());
40977         
40978         if(v || v === 0 || v === ''){
40979             this.setValue(v);
40980         }
40981     },
40982     
40983     hiddenEl : function()
40984     {
40985         return this.el.select('input.hidden-number-input',true).first();
40986     }
40987     
40988 });
40989
40990  
40991
40992 /*
40993 * Licence: LGPL
40994 */
40995
40996 /**
40997  * @class Roo.bootstrap.DocumentSlider
40998  * @extends Roo.bootstrap.Component
40999  * Bootstrap DocumentSlider class
41000  * 
41001  * @constructor
41002  * Create a new DocumentViewer
41003  * @param {Object} config The config object
41004  */
41005
41006 Roo.bootstrap.DocumentSlider = function(config){
41007     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41008     
41009     this.files = [];
41010     
41011     this.addEvents({
41012         /**
41013          * @event initial
41014          * Fire after initEvent
41015          * @param {Roo.bootstrap.DocumentSlider} this
41016          */
41017         "initial" : true,
41018         /**
41019          * @event update
41020          * Fire after update
41021          * @param {Roo.bootstrap.DocumentSlider} this
41022          */
41023         "update" : true,
41024         /**
41025          * @event click
41026          * Fire after click
41027          * @param {Roo.bootstrap.DocumentSlider} this
41028          */
41029         "click" : true
41030     });
41031 };
41032
41033 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
41034     
41035     files : false,
41036     
41037     indicator : 0,
41038     
41039     getAutoCreate : function()
41040     {
41041         var cfg = {
41042             tag : 'div',
41043             cls : 'roo-document-slider',
41044             cn : [
41045                 {
41046                     tag : 'div',
41047                     cls : 'roo-document-slider-header',
41048                     cn : [
41049                         {
41050                             tag : 'div',
41051                             cls : 'roo-document-slider-header-title'
41052                         }
41053                     ]
41054                 },
41055                 {
41056                     tag : 'div',
41057                     cls : 'roo-document-slider-body',
41058                     cn : [
41059                         {
41060                             tag : 'div',
41061                             cls : 'roo-document-slider-prev',
41062                             cn : [
41063                                 {
41064                                     tag : 'i',
41065                                     cls : 'fa fa-chevron-left'
41066                                 }
41067                             ]
41068                         },
41069                         {
41070                             tag : 'div',
41071                             cls : 'roo-document-slider-thumb',
41072                             cn : [
41073                                 {
41074                                     tag : 'img',
41075                                     cls : 'roo-document-slider-image'
41076                                 }
41077                             ]
41078                         },
41079                         {
41080                             tag : 'div',
41081                             cls : 'roo-document-slider-next',
41082                             cn : [
41083                                 {
41084                                     tag : 'i',
41085                                     cls : 'fa fa-chevron-right'
41086                                 }
41087                             ]
41088                         }
41089                     ]
41090                 }
41091             ]
41092         };
41093         
41094         return cfg;
41095     },
41096     
41097     initEvents : function()
41098     {
41099         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41100         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41101         
41102         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41103         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41104         
41105         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41106         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41107         
41108         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41109         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41110         
41111         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41112         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41113         
41114         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41115         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41116         
41117         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41118         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41119         
41120         this.thumbEl.on('click', this.onClick, this);
41121         
41122         this.prevIndicator.on('click', this.prev, this);
41123         
41124         this.nextIndicator.on('click', this.next, this);
41125         
41126     },
41127     
41128     initial : function()
41129     {
41130         if(this.files.length){
41131             this.indicator = 1;
41132             this.update()
41133         }
41134         
41135         this.fireEvent('initial', this);
41136     },
41137     
41138     update : function()
41139     {
41140         this.imageEl.attr('src', this.files[this.indicator - 1]);
41141         
41142         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
41143         
41144         this.prevIndicator.show();
41145         
41146         if(this.indicator == 1){
41147             this.prevIndicator.hide();
41148         }
41149         
41150         this.nextIndicator.show();
41151         
41152         if(this.indicator == this.files.length){
41153             this.nextIndicator.hide();
41154         }
41155         
41156         this.thumbEl.scrollTo('top');
41157         
41158         this.fireEvent('update', this);
41159     },
41160     
41161     onClick : function(e)
41162     {
41163         e.preventDefault();
41164         
41165         this.fireEvent('click', this);
41166     },
41167     
41168     prev : function(e)
41169     {
41170         e.preventDefault();
41171         
41172         this.indicator = Math.max(1, this.indicator - 1);
41173         
41174         this.update();
41175     },
41176     
41177     next : function(e)
41178     {
41179         e.preventDefault();
41180         
41181         this.indicator = Math.min(this.files.length, this.indicator + 1);
41182         
41183         this.update();
41184     }
41185 });
41186 /*
41187  * - LGPL
41188  *
41189  * RadioSet
41190  *
41191  *
41192  */
41193
41194 /**
41195  * @class Roo.bootstrap.form.RadioSet
41196  * @extends Roo.bootstrap.form.Input
41197  * @children Roo.bootstrap.form.Radio
41198  * Bootstrap RadioSet class
41199  * @cfg {String} indicatorpos (left|right) default left
41200  * @cfg {Boolean} inline (true|false) inline the element (default true)
41201  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
41202  * @constructor
41203  * Create a new RadioSet
41204  * @param {Object} config The config object
41205  */
41206
41207 Roo.bootstrap.form.RadioSet = function(config){
41208     
41209     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
41210     
41211     this.radioes = [];
41212     
41213     Roo.bootstrap.form.RadioSet.register(this);
41214     
41215     this.addEvents({
41216         /**
41217         * @event check
41218         * Fires when the element is checked or unchecked.
41219         * @param {Roo.bootstrap.form.RadioSet} this This radio
41220         * @param {Roo.bootstrap.form.Radio} item The checked item
41221         */
41222        check : true,
41223        /**
41224         * @event click
41225         * Fires when the element is click.
41226         * @param {Roo.bootstrap.form.RadioSet} this This radio set
41227         * @param {Roo.bootstrap.form.Radio} item The checked item
41228         * @param {Roo.EventObject} e The event object
41229         */
41230        click : true
41231     });
41232     
41233 };
41234
41235 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
41236
41237     radioes : false,
41238     
41239     inline : true,
41240     
41241     weight : '',
41242     
41243     indicatorpos : 'left',
41244     
41245     getAutoCreate : function()
41246     {
41247         var label = {
41248             tag : 'label',
41249             cls : 'roo-radio-set-label',
41250             cn : [
41251                 {
41252                     tag : 'span',
41253                     html : this.fieldLabel
41254                 }
41255             ]
41256         };
41257         if (Roo.bootstrap.version == 3) {
41258             
41259             
41260             if(this.indicatorpos == 'left'){
41261                 label.cn.unshift({
41262                     tag : 'i',
41263                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
41264                     tooltip : 'This field is required'
41265                 });
41266             } else {
41267                 label.cn.push({
41268                     tag : 'i',
41269                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
41270                     tooltip : 'This field is required'
41271                 });
41272             }
41273         }
41274         var items = {
41275             tag : 'div',
41276             cls : 'roo-radio-set-items'
41277         };
41278         
41279         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
41280         
41281         if (align === 'left' && this.fieldLabel.length) {
41282             
41283             items = {
41284                 cls : "roo-radio-set-right", 
41285                 cn: [
41286                     items
41287                 ]
41288             };
41289             
41290             if(this.labelWidth > 12){
41291                 label.style = "width: " + this.labelWidth + 'px';
41292             }
41293             
41294             if(this.labelWidth < 13 && this.labelmd == 0){
41295                 this.labelmd = this.labelWidth;
41296             }
41297             
41298             if(this.labellg > 0){
41299                 label.cls += ' col-lg-' + this.labellg;
41300                 items.cls += ' col-lg-' + (12 - this.labellg);
41301             }
41302             
41303             if(this.labelmd > 0){
41304                 label.cls += ' col-md-' + this.labelmd;
41305                 items.cls += ' col-md-' + (12 - this.labelmd);
41306             }
41307             
41308             if(this.labelsm > 0){
41309                 label.cls += ' col-sm-' + this.labelsm;
41310                 items.cls += ' col-sm-' + (12 - this.labelsm);
41311             }
41312             
41313             if(this.labelxs > 0){
41314                 label.cls += ' col-xs-' + this.labelxs;
41315                 items.cls += ' col-xs-' + (12 - this.labelxs);
41316             }
41317         }
41318         
41319         var cfg = {
41320             tag : 'div',
41321             cls : 'roo-radio-set',
41322             cn : [
41323                 {
41324                     tag : 'input',
41325                     cls : 'roo-radio-set-input',
41326                     type : 'hidden',
41327                     name : this.name,
41328                     value : this.value ? this.value :  ''
41329                 },
41330                 label,
41331                 items
41332             ]
41333         };
41334         
41335         if(this.weight.length){
41336             cfg.cls += ' roo-radio-' + this.weight;
41337         }
41338         
41339         if(this.inline) {
41340             cfg.cls += ' roo-radio-set-inline';
41341         }
41342         
41343         var settings=this;
41344         ['xs','sm','md','lg'].map(function(size){
41345             if (settings[size]) {
41346                 cfg.cls += ' col-' + size + '-' + settings[size];
41347             }
41348         });
41349         
41350         return cfg;
41351         
41352     },
41353
41354     initEvents : function()
41355     {
41356         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
41357         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
41358         
41359         if(!this.fieldLabel.length){
41360             this.labelEl.hide();
41361         }
41362         
41363         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
41364         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
41365         
41366         this.indicator = this.indicatorEl();
41367         
41368         if(this.indicator){
41369             this.indicator.addClass('invisible');
41370         }
41371         
41372         this.originalValue = this.getValue();
41373         
41374     },
41375     
41376     inputEl: function ()
41377     {
41378         return this.el.select('.roo-radio-set-input', true).first();
41379     },
41380     
41381     getChildContainer : function()
41382     {
41383         return this.itemsEl;
41384     },
41385     
41386     register : function(item)
41387     {
41388         this.radioes.push(item);
41389         
41390     },
41391     
41392     validate : function()
41393     {   
41394         if(this.getVisibilityEl().hasClass('hidden')){
41395             return true;
41396         }
41397         
41398         var valid = false;
41399         
41400         Roo.each(this.radioes, function(i){
41401             if(!i.checked){
41402                 return;
41403             }
41404             
41405             valid = true;
41406             return false;
41407         });
41408         
41409         if(this.allowBlank) {
41410             return true;
41411         }
41412         
41413         if(this.disabled || valid){
41414             this.markValid();
41415             return true;
41416         }
41417         
41418         this.markInvalid();
41419         return false;
41420         
41421     },
41422     
41423     markValid : function()
41424     {
41425         if(this.labelEl.isVisible(true) && this.indicatorEl()){
41426             this.indicatorEl().removeClass('visible');
41427             this.indicatorEl().addClass('invisible');
41428         }
41429         
41430         
41431         if (Roo.bootstrap.version == 3) {
41432             this.el.removeClass([this.invalidClass, this.validClass]);
41433             this.el.addClass(this.validClass);
41434         } else {
41435             this.el.removeClass(['is-invalid','is-valid']);
41436             this.el.addClass(['is-valid']);
41437         }
41438         this.fireEvent('valid', this);
41439     },
41440     
41441     markInvalid : function(msg)
41442     {
41443         if(this.allowBlank || this.disabled){
41444             return;
41445         }
41446         
41447         if(this.labelEl.isVisible(true) && this.indicatorEl()){
41448             this.indicatorEl().removeClass('invisible');
41449             this.indicatorEl().addClass('visible');
41450         }
41451         if (Roo.bootstrap.version == 3) {
41452             this.el.removeClass([this.invalidClass, this.validClass]);
41453             this.el.addClass(this.invalidClass);
41454         } else {
41455             this.el.removeClass(['is-invalid','is-valid']);
41456             this.el.addClass(['is-invalid']);
41457         }
41458         
41459         this.fireEvent('invalid', this, msg);
41460         
41461     },
41462     
41463     setValue : function(v, suppressEvent)
41464     {   
41465         if(this.value === v){
41466             return;
41467         }
41468         
41469         this.value = v;
41470         
41471         if(this.rendered){
41472             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41473         }
41474         
41475         Roo.each(this.radioes, function(i){
41476             i.checked = false;
41477             i.el.removeClass('checked');
41478         });
41479         
41480         Roo.each(this.radioes, function(i){
41481             
41482             if(i.value === v || i.value.toString() === v.toString()){
41483                 i.checked = true;
41484                 i.el.addClass('checked');
41485                 
41486                 if(suppressEvent !== true){
41487                     this.fireEvent('check', this, i);
41488                 }
41489                 
41490                 return false;
41491             }
41492             
41493         }, this);
41494         
41495         this.validate();
41496     },
41497     
41498     clearInvalid : function(){
41499         
41500         if(!this.el || this.preventMark){
41501             return;
41502         }
41503         
41504         this.el.removeClass([this.invalidClass]);
41505         
41506         this.fireEvent('valid', this);
41507     }
41508     
41509 });
41510
41511 Roo.apply(Roo.bootstrap.form.RadioSet, {
41512     
41513     groups: {},
41514     
41515     register : function(set)
41516     {
41517         this.groups[set.name] = set;
41518     },
41519     
41520     get: function(name) 
41521     {
41522         if (typeof(this.groups[name]) == 'undefined') {
41523             return false;
41524         }
41525         
41526         return this.groups[name] ;
41527     }
41528     
41529 });
41530 /*
41531  * Based on:
41532  * Ext JS Library 1.1.1
41533  * Copyright(c) 2006-2007, Ext JS, LLC.
41534  *
41535  * Originally Released Under LGPL - original licence link has changed is not relivant.
41536  *
41537  * Fork - LGPL
41538  * <script type="text/javascript">
41539  */
41540
41541
41542 /**
41543  * @class Roo.bootstrap.SplitBar
41544  * @extends Roo.util.Observable
41545  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
41546  * <br><br>
41547  * Usage:
41548  * <pre><code>
41549 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
41550                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
41551 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
41552 split.minSize = 100;
41553 split.maxSize = 600;
41554 split.animate = true;
41555 split.on('moved', splitterMoved);
41556 </code></pre>
41557  * @constructor
41558  * Create a new SplitBar
41559  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
41560  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
41561  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
41562  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
41563                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
41564                         position of the SplitBar).
41565  */
41566 Roo.bootstrap.SplitBar = function(cfg){
41567     
41568     /** @private */
41569     
41570     //{
41571     //  dragElement : elm
41572     //  resizingElement: el,
41573         // optional..
41574     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
41575     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
41576         // existingProxy ???
41577     //}
41578     
41579     this.el = Roo.get(cfg.dragElement, true);
41580     this.el.dom.unselectable = "on";
41581     /** @private */
41582     this.resizingEl = Roo.get(cfg.resizingElement, true);
41583
41584     /**
41585      * @private
41586      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
41587      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
41588      * @type Number
41589      */
41590     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
41591     
41592     /**
41593      * The minimum size of the resizing element. (Defaults to 0)
41594      * @type Number
41595      */
41596     this.minSize = 0;
41597     
41598     /**
41599      * The maximum size of the resizing element. (Defaults to 2000)
41600      * @type Number
41601      */
41602     this.maxSize = 2000;
41603     
41604     /**
41605      * Whether to animate the transition to the new size
41606      * @type Boolean
41607      */
41608     this.animate = false;
41609     
41610     /**
41611      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
41612      * @type Boolean
41613      */
41614     this.useShim = false;
41615     
41616     /** @private */
41617     this.shim = null;
41618     
41619     if(!cfg.existingProxy){
41620         /** @private */
41621         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
41622     }else{
41623         this.proxy = Roo.get(cfg.existingProxy).dom;
41624     }
41625     /** @private */
41626     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
41627     
41628     /** @private */
41629     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
41630     
41631     /** @private */
41632     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
41633     
41634     /** @private */
41635     this.dragSpecs = {};
41636     
41637     /**
41638      * @private The adapter to use to positon and resize elements
41639      */
41640     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41641     this.adapter.init(this);
41642     
41643     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41644         /** @private */
41645         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
41646         this.el.addClass("roo-splitbar-h");
41647     }else{
41648         /** @private */
41649         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
41650         this.el.addClass("roo-splitbar-v");
41651     }
41652     
41653     this.addEvents({
41654         /**
41655          * @event resize
41656          * Fires when the splitter is moved (alias for {@link #event-moved})
41657          * @param {Roo.bootstrap.SplitBar} this
41658          * @param {Number} newSize the new width or height
41659          */
41660         "resize" : true,
41661         /**
41662          * @event moved
41663          * Fires when the splitter is moved
41664          * @param {Roo.bootstrap.SplitBar} this
41665          * @param {Number} newSize the new width or height
41666          */
41667         "moved" : true,
41668         /**
41669          * @event beforeresize
41670          * Fires before the splitter is dragged
41671          * @param {Roo.bootstrap.SplitBar} this
41672          */
41673         "beforeresize" : true,
41674
41675         "beforeapply" : true
41676     });
41677
41678     Roo.util.Observable.call(this);
41679 };
41680
41681 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
41682     onStartProxyDrag : function(x, y){
41683         this.fireEvent("beforeresize", this);
41684         if(!this.overlay){
41685             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
41686             o.unselectable();
41687             o.enableDisplayMode("block");
41688             // all splitbars share the same overlay
41689             Roo.bootstrap.SplitBar.prototype.overlay = o;
41690         }
41691         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
41692         this.overlay.show();
41693         Roo.get(this.proxy).setDisplayed("block");
41694         var size = this.adapter.getElementSize(this);
41695         this.activeMinSize = this.getMinimumSize();;
41696         this.activeMaxSize = this.getMaximumSize();;
41697         var c1 = size - this.activeMinSize;
41698         var c2 = Math.max(this.activeMaxSize - size, 0);
41699         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41700             this.dd.resetConstraints();
41701             this.dd.setXConstraint(
41702                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
41703                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
41704             );
41705             this.dd.setYConstraint(0, 0);
41706         }else{
41707             this.dd.resetConstraints();
41708             this.dd.setXConstraint(0, 0);
41709             this.dd.setYConstraint(
41710                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
41711                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
41712             );
41713          }
41714         this.dragSpecs.startSize = size;
41715         this.dragSpecs.startPoint = [x, y];
41716         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
41717     },
41718     
41719     /** 
41720      * @private Called after the drag operation by the DDProxy
41721      */
41722     onEndProxyDrag : function(e){
41723         Roo.get(this.proxy).setDisplayed(false);
41724         var endPoint = Roo.lib.Event.getXY(e);
41725         if(this.overlay){
41726             this.overlay.hide();
41727         }
41728         var newSize;
41729         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41730             newSize = this.dragSpecs.startSize + 
41731                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
41732                     endPoint[0] - this.dragSpecs.startPoint[0] :
41733                     this.dragSpecs.startPoint[0] - endPoint[0]
41734                 );
41735         }else{
41736             newSize = this.dragSpecs.startSize + 
41737                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
41738                     endPoint[1] - this.dragSpecs.startPoint[1] :
41739                     this.dragSpecs.startPoint[1] - endPoint[1]
41740                 );
41741         }
41742         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
41743         if(newSize != this.dragSpecs.startSize){
41744             if(this.fireEvent('beforeapply', this, newSize) !== false){
41745                 this.adapter.setElementSize(this, newSize);
41746                 this.fireEvent("moved", this, newSize);
41747                 this.fireEvent("resize", this, newSize);
41748             }
41749         }
41750     },
41751     
41752     /**
41753      * Get the adapter this SplitBar uses
41754      * @return The adapter object
41755      */
41756     getAdapter : function(){
41757         return this.adapter;
41758     },
41759     
41760     /**
41761      * Set the adapter this SplitBar uses
41762      * @param {Object} adapter A SplitBar adapter object
41763      */
41764     setAdapter : function(adapter){
41765         this.adapter = adapter;
41766         this.adapter.init(this);
41767     },
41768     
41769     /**
41770      * Gets the minimum size for the resizing element
41771      * @return {Number} The minimum size
41772      */
41773     getMinimumSize : function(){
41774         return this.minSize;
41775     },
41776     
41777     /**
41778      * Sets the minimum size for the resizing element
41779      * @param {Number} minSize The minimum size
41780      */
41781     setMinimumSize : function(minSize){
41782         this.minSize = minSize;
41783     },
41784     
41785     /**
41786      * Gets the maximum size for the resizing element
41787      * @return {Number} The maximum size
41788      */
41789     getMaximumSize : function(){
41790         return this.maxSize;
41791     },
41792     
41793     /**
41794      * Sets the maximum size for the resizing element
41795      * @param {Number} maxSize The maximum size
41796      */
41797     setMaximumSize : function(maxSize){
41798         this.maxSize = maxSize;
41799     },
41800     
41801     /**
41802      * Sets the initialize size for the resizing element
41803      * @param {Number} size The initial size
41804      */
41805     setCurrentSize : function(size){
41806         var oldAnimate = this.animate;
41807         this.animate = false;
41808         this.adapter.setElementSize(this, size);
41809         this.animate = oldAnimate;
41810     },
41811     
41812     /**
41813      * Destroy this splitbar. 
41814      * @param {Boolean} removeEl True to remove the element
41815      */
41816     destroy : function(removeEl){
41817         if(this.shim){
41818             this.shim.remove();
41819         }
41820         this.dd.unreg();
41821         this.proxy.parentNode.removeChild(this.proxy);
41822         if(removeEl){
41823             this.el.remove();
41824         }
41825     }
41826 });
41827
41828 /**
41829  * @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.
41830  */
41831 Roo.bootstrap.SplitBar.createProxy = function(dir){
41832     var proxy = new Roo.Element(document.createElement("div"));
41833     proxy.unselectable();
41834     var cls = 'roo-splitbar-proxy';
41835     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
41836     document.body.appendChild(proxy.dom);
41837     return proxy.dom;
41838 };
41839
41840 /** 
41841  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
41842  * Default Adapter. It assumes the splitter and resizing element are not positioned
41843  * elements and only gets/sets the width of the element. Generally used for table based layouts.
41844  */
41845 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
41846 };
41847
41848 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
41849     // do nothing for now
41850     init : function(s){
41851     
41852     },
41853     /**
41854      * Called before drag operations to get the current size of the resizing element. 
41855      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41856      */
41857      getElementSize : function(s){
41858         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41859             return s.resizingEl.getWidth();
41860         }else{
41861             return s.resizingEl.getHeight();
41862         }
41863     },
41864     
41865     /**
41866      * Called after drag operations to set the size of the resizing element.
41867      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41868      * @param {Number} newSize The new size to set
41869      * @param {Function} onComplete A function to be invoked when resizing is complete
41870      */
41871     setElementSize : function(s, newSize, onComplete){
41872         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41873             if(!s.animate){
41874                 s.resizingEl.setWidth(newSize);
41875                 if(onComplete){
41876                     onComplete(s, newSize);
41877                 }
41878             }else{
41879                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
41880             }
41881         }else{
41882             
41883             if(!s.animate){
41884                 s.resizingEl.setHeight(newSize);
41885                 if(onComplete){
41886                     onComplete(s, newSize);
41887                 }
41888             }else{
41889                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
41890             }
41891         }
41892     }
41893 };
41894
41895 /** 
41896  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
41897  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
41898  * Adapter that  moves the splitter element to align with the resized sizing element. 
41899  * Used with an absolute positioned SplitBar.
41900  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
41901  * document.body, make sure you assign an id to the body element.
41902  */
41903 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
41904     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41905     this.container = Roo.get(container);
41906 };
41907
41908 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
41909     init : function(s){
41910         this.basic.init(s);
41911     },
41912     
41913     getElementSize : function(s){
41914         return this.basic.getElementSize(s);
41915     },
41916     
41917     setElementSize : function(s, newSize, onComplete){
41918         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
41919     },
41920     
41921     moveSplitter : function(s){
41922         var yes = Roo.bootstrap.SplitBar;
41923         switch(s.placement){
41924             case yes.LEFT:
41925                 s.el.setX(s.resizingEl.getRight());
41926                 break;
41927             case yes.RIGHT:
41928                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
41929                 break;
41930             case yes.TOP:
41931                 s.el.setY(s.resizingEl.getBottom());
41932                 break;
41933             case yes.BOTTOM:
41934                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
41935                 break;
41936         }
41937     }
41938 };
41939
41940 /**
41941  * Orientation constant - Create a vertical SplitBar
41942  * @static
41943  * @type Number
41944  */
41945 Roo.bootstrap.SplitBar.VERTICAL = 1;
41946
41947 /**
41948  * Orientation constant - Create a horizontal SplitBar
41949  * @static
41950  * @type Number
41951  */
41952 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
41953
41954 /**
41955  * Placement constant - The resizing element is to the left of the splitter element
41956  * @static
41957  * @type Number
41958  */
41959 Roo.bootstrap.SplitBar.LEFT = 1;
41960
41961 /**
41962  * Placement constant - The resizing element is to the right of the splitter element
41963  * @static
41964  * @type Number
41965  */
41966 Roo.bootstrap.SplitBar.RIGHT = 2;
41967
41968 /**
41969  * Placement constant - The resizing element is positioned above the splitter element
41970  * @static
41971  * @type Number
41972  */
41973 Roo.bootstrap.SplitBar.TOP = 3;
41974
41975 /**
41976  * Placement constant - The resizing element is positioned under splitter element
41977  * @static
41978  * @type Number
41979  */
41980 Roo.bootstrap.SplitBar.BOTTOM = 4;
41981 /*
41982  * Based on:
41983  * Ext JS Library 1.1.1
41984  * Copyright(c) 2006-2007, Ext JS, LLC.
41985  *
41986  * Originally Released Under LGPL - original licence link has changed is not relivant.
41987  *
41988  * Fork - LGPL
41989  * <script type="text/javascript">
41990  */
41991
41992 /**
41993  * @class Roo.bootstrap.layout.Manager
41994  * @extends Roo.bootstrap.Component
41995  * @abstract
41996  * Base class for layout managers.
41997  */
41998 Roo.bootstrap.layout.Manager = function(config)
41999 {
42000     this.monitorWindowResize = true; // do this before we apply configuration.
42001     
42002     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42003
42004
42005
42006
42007
42008     /** false to disable window resize monitoring @type Boolean */
42009     
42010     this.regions = {};
42011     this.addEvents({
42012         /**
42013          * @event layout
42014          * Fires when a layout is performed.
42015          * @param {Roo.LayoutManager} this
42016          */
42017         "layout" : true,
42018         /**
42019          * @event regionresized
42020          * Fires when the user resizes a region.
42021          * @param {Roo.LayoutRegion} region The resized region
42022          * @param {Number} newSize The new size (width for east/west, height for north/south)
42023          */
42024         "regionresized" : true,
42025         /**
42026          * @event regioncollapsed
42027          * Fires when a region is collapsed.
42028          * @param {Roo.LayoutRegion} region The collapsed region
42029          */
42030         "regioncollapsed" : true,
42031         /**
42032          * @event regionexpanded
42033          * Fires when a region is expanded.
42034          * @param {Roo.LayoutRegion} region The expanded region
42035          */
42036         "regionexpanded" : true
42037     });
42038     this.updating = false;
42039
42040     if (config.el) {
42041         this.el = Roo.get(config.el);
42042         this.initEvents();
42043     }
42044
42045 };
42046
42047 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42048
42049
42050     regions : null,
42051
42052     monitorWindowResize : true,
42053
42054
42055     updating : false,
42056
42057
42058     onRender : function(ct, position)
42059     {
42060         if(!this.el){
42061             this.el = Roo.get(ct);
42062             this.initEvents();
42063         }
42064         //this.fireEvent('render',this);
42065     },
42066
42067
42068     initEvents: function()
42069     {
42070
42071
42072         // ie scrollbar fix
42073         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42074             document.body.scroll = "no";
42075         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42076             this.el.position('relative');
42077         }
42078         this.id = this.el.id;
42079         this.el.addClass("roo-layout-container");
42080         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42081         if(this.el.dom != document.body ) {
42082             this.el.on('resize', this.layout,this);
42083             this.el.on('show', this.layout,this);
42084         }
42085
42086     },
42087
42088     /**
42089      * Returns true if this layout is currently being updated
42090      * @return {Boolean}
42091      */
42092     isUpdating : function(){
42093         return this.updating;
42094     },
42095
42096     /**
42097      * Suspend the LayoutManager from doing auto-layouts while
42098      * making multiple add or remove calls
42099      */
42100     beginUpdate : function(){
42101         this.updating = true;
42102     },
42103
42104     /**
42105      * Restore auto-layouts and optionally disable the manager from performing a layout
42106      * @param {Boolean} noLayout true to disable a layout update
42107      */
42108     endUpdate : function(noLayout){
42109         this.updating = false;
42110         if(!noLayout){
42111             this.layout();
42112         }
42113     },
42114
42115     layout: function(){
42116         // abstract...
42117     },
42118
42119     onRegionResized : function(region, newSize){
42120         this.fireEvent("regionresized", region, newSize);
42121         this.layout();
42122     },
42123
42124     onRegionCollapsed : function(region){
42125         this.fireEvent("regioncollapsed", region);
42126     },
42127
42128     onRegionExpanded : function(region){
42129         this.fireEvent("regionexpanded", region);
42130     },
42131
42132     /**
42133      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42134      * performs box-model adjustments.
42135      * @return {Object} The size as an object {width: (the width), height: (the height)}
42136      */
42137     getViewSize : function()
42138     {
42139         var size;
42140         if(this.el.dom != document.body){
42141             size = this.el.getSize();
42142         }else{
42143             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42144         }
42145         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42146         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42147         return size;
42148     },
42149
42150     /**
42151      * Returns the Element this layout is bound to.
42152      * @return {Roo.Element}
42153      */
42154     getEl : function(){
42155         return this.el;
42156     },
42157
42158     /**
42159      * Returns the specified region.
42160      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42161      * @return {Roo.LayoutRegion}
42162      */
42163     getRegion : function(target){
42164         return this.regions[target.toLowerCase()];
42165     },
42166
42167     onWindowResize : function(){
42168         if(this.monitorWindowResize){
42169             this.layout();
42170         }
42171     }
42172 });
42173 /*
42174  * Based on:
42175  * Ext JS Library 1.1.1
42176  * Copyright(c) 2006-2007, Ext JS, LLC.
42177  *
42178  * Originally Released Under LGPL - original licence link has changed is not relivant.
42179  *
42180  * Fork - LGPL
42181  * <script type="text/javascript">
42182  */
42183 /**
42184  * @class Roo.bootstrap.layout.Border
42185  * @extends Roo.bootstrap.layout.Manager
42186  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
42187  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
42188  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42189  * please see: examples/bootstrap/nested.html<br><br>
42190  
42191 <b>The container the layout is rendered into can be either the body element or any other element.
42192 If it is not the body element, the container needs to either be an absolute positioned element,
42193 or you will need to add "position:relative" to the css of the container.  You will also need to specify
42194 the container size if it is not the body element.</b>
42195
42196 * @constructor
42197 * Create a new Border
42198 * @param {Object} config Configuration options
42199  */
42200 Roo.bootstrap.layout.Border = function(config){
42201     config = config || {};
42202     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
42203     
42204     
42205     
42206     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42207         if(config[region]){
42208             config[region].region = region;
42209             this.addRegion(config[region]);
42210         }
42211     },this);
42212     
42213 };
42214
42215 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
42216
42217 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
42218     
42219         /**
42220          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
42221          */
42222         /**
42223          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
42224          */
42225         /**
42226          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
42227          */
42228         /**
42229          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
42230          */
42231         /**
42232          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
42233          */
42234         
42235         
42236         
42237         
42238     parent : false, // this might point to a 'nest' or a ???
42239     
42240     /**
42241      * Creates and adds a new region if it doesn't already exist.
42242      * @param {String} target The target region key (north, south, east, west or center).
42243      * @param {Object} config The regions config object
42244      * @return {BorderLayoutRegion} The new region
42245      */
42246     addRegion : function(config)
42247     {
42248         if(!this.regions[config.region]){
42249             var r = this.factory(config);
42250             this.bindRegion(r);
42251         }
42252         return this.regions[config.region];
42253     },
42254
42255     // private (kinda)
42256     bindRegion : function(r){
42257         this.regions[r.config.region] = r;
42258         
42259         r.on("visibilitychange",    this.layout, this);
42260         r.on("paneladded",          this.layout, this);
42261         r.on("panelremoved",        this.layout, this);
42262         r.on("invalidated",         this.layout, this);
42263         r.on("resized",             this.onRegionResized, this);
42264         r.on("collapsed",           this.onRegionCollapsed, this);
42265         r.on("expanded",            this.onRegionExpanded, this);
42266     },
42267
42268     /**
42269      * Performs a layout update.
42270      */
42271     layout : function()
42272     {
42273         if(this.updating) {
42274             return;
42275         }
42276         
42277         // render all the rebions if they have not been done alreayd?
42278         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42279             if(this.regions[region] && !this.regions[region].bodyEl){
42280                 this.regions[region].onRender(this.el)
42281             }
42282         },this);
42283         
42284         var size = this.getViewSize();
42285         var w = size.width;
42286         var h = size.height;
42287         var centerW = w;
42288         var centerH = h;
42289         var centerY = 0;
42290         var centerX = 0;
42291         //var x = 0, y = 0;
42292
42293         var rs = this.regions;
42294         var north = rs["north"];
42295         var south = rs["south"]; 
42296         var west = rs["west"];
42297         var east = rs["east"];
42298         var center = rs["center"];
42299         //if(this.hideOnLayout){ // not supported anymore
42300             //c.el.setStyle("display", "none");
42301         //}
42302         if(north && north.isVisible()){
42303             var b = north.getBox();
42304             var m = north.getMargins();
42305             b.width = w - (m.left+m.right);
42306             b.x = m.left;
42307             b.y = m.top;
42308             centerY = b.height + b.y + m.bottom;
42309             centerH -= centerY;
42310             north.updateBox(this.safeBox(b));
42311         }
42312         if(south && south.isVisible()){
42313             var b = south.getBox();
42314             var m = south.getMargins();
42315             b.width = w - (m.left+m.right);
42316             b.x = m.left;
42317             var totalHeight = (b.height + m.top + m.bottom);
42318             b.y = h - totalHeight + m.top;
42319             centerH -= totalHeight;
42320             south.updateBox(this.safeBox(b));
42321         }
42322         if(west && west.isVisible()){
42323             var b = west.getBox();
42324             var m = west.getMargins();
42325             b.height = centerH - (m.top+m.bottom);
42326             b.x = m.left;
42327             b.y = centerY + m.top;
42328             var totalWidth = (b.width + m.left + m.right);
42329             centerX += totalWidth;
42330             centerW -= totalWidth;
42331             west.updateBox(this.safeBox(b));
42332         }
42333         if(east && east.isVisible()){
42334             var b = east.getBox();
42335             var m = east.getMargins();
42336             b.height = centerH - (m.top+m.bottom);
42337             var totalWidth = (b.width + m.left + m.right);
42338             b.x = w - totalWidth + m.left;
42339             b.y = centerY + m.top;
42340             centerW -= totalWidth;
42341             east.updateBox(this.safeBox(b));
42342         }
42343         if(center){
42344             var m = center.getMargins();
42345             var centerBox = {
42346                 x: centerX + m.left,
42347                 y: centerY + m.top,
42348                 width: centerW - (m.left+m.right),
42349                 height: centerH - (m.top+m.bottom)
42350             };
42351             //if(this.hideOnLayout){
42352                 //center.el.setStyle("display", "block");
42353             //}
42354             center.updateBox(this.safeBox(centerBox));
42355         }
42356         this.el.repaint();
42357         this.fireEvent("layout", this);
42358     },
42359
42360     // private
42361     safeBox : function(box){
42362         box.width = Math.max(0, box.width);
42363         box.height = Math.max(0, box.height);
42364         return box;
42365     },
42366
42367     /**
42368      * Adds a ContentPanel (or subclass) to this layout.
42369      * @param {String} target The target region key (north, south, east, west or center).
42370      * @param {Roo.ContentPanel} panel The panel to add
42371      * @return {Roo.ContentPanel} The added panel
42372      */
42373     add : function(target, panel){
42374          
42375         target = target.toLowerCase();
42376         return this.regions[target].add(panel);
42377     },
42378
42379     /**
42380      * Remove a ContentPanel (or subclass) to this layout.
42381      * @param {String} target The target region key (north, south, east, west or center).
42382      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42383      * @return {Roo.ContentPanel} The removed panel
42384      */
42385     remove : function(target, panel){
42386         target = target.toLowerCase();
42387         return this.regions[target].remove(panel);
42388     },
42389
42390     /**
42391      * Searches all regions for a panel with the specified id
42392      * @param {String} panelId
42393      * @return {Roo.ContentPanel} The panel or null if it wasn't found
42394      */
42395     findPanel : function(panelId){
42396         var rs = this.regions;
42397         for(var target in rs){
42398             if(typeof rs[target] != "function"){
42399                 var p = rs[target].getPanel(panelId);
42400                 if(p){
42401                     return p;
42402                 }
42403             }
42404         }
42405         return null;
42406     },
42407
42408     /**
42409      * Searches all regions for a panel with the specified id and activates (shows) it.
42410      * @param {String/ContentPanel} panelId The panels id or the panel itself
42411      * @return {Roo.ContentPanel} The shown panel or null
42412      */
42413     showPanel : function(panelId) {
42414       var rs = this.regions;
42415       for(var target in rs){
42416          var r = rs[target];
42417          if(typeof r != "function"){
42418             if(r.hasPanel(panelId)){
42419                return r.showPanel(panelId);
42420             }
42421          }
42422       }
42423       return null;
42424    },
42425
42426    /**
42427      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42428      * @param {Roo.state.Provider} provider (optional) An alternate state provider
42429      */
42430    /*
42431     restoreState : function(provider){
42432         if(!provider){
42433             provider = Roo.state.Manager;
42434         }
42435         var sm = new Roo.LayoutStateManager();
42436         sm.init(this, provider);
42437     },
42438 */
42439  
42440  
42441     /**
42442      * Adds a xtype elements to the layout.
42443      * <pre><code>
42444
42445 layout.addxtype({
42446        xtype : 'ContentPanel',
42447        region: 'west',
42448        items: [ .... ]
42449    }
42450 );
42451
42452 layout.addxtype({
42453         xtype : 'NestedLayoutPanel',
42454         region: 'west',
42455         layout: {
42456            center: { },
42457            west: { }   
42458         },
42459         items : [ ... list of content panels or nested layout panels.. ]
42460    }
42461 );
42462 </code></pre>
42463      * @param {Object} cfg Xtype definition of item to add.
42464      */
42465     addxtype : function(cfg)
42466     {
42467         // basically accepts a pannel...
42468         // can accept a layout region..!?!?
42469         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
42470         
42471         
42472         // theory?  children can only be panels??
42473         
42474         //if (!cfg.xtype.match(/Panel$/)) {
42475         //    return false;
42476         //}
42477         var ret = false;
42478         
42479         if (typeof(cfg.region) == 'undefined') {
42480             Roo.log("Failed to add Panel, region was not set");
42481             Roo.log(cfg);
42482             return false;
42483         }
42484         var region = cfg.region;
42485         delete cfg.region;
42486         
42487           
42488         var xitems = [];
42489         if (cfg.items) {
42490             xitems = cfg.items;
42491             delete cfg.items;
42492         }
42493         var nb = false;
42494         
42495         if ( region == 'center') {
42496             Roo.log("Center: " + cfg.title);
42497         }
42498         
42499         
42500         switch(cfg.xtype) 
42501         {
42502             case 'Content':  // ContentPanel (el, cfg)
42503             case 'Scroll':  // ContentPanel (el, cfg)
42504             case 'View': 
42505                 cfg.autoCreate = cfg.autoCreate || true;
42506                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42507                 //} else {
42508                 //    var el = this.el.createChild();
42509                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
42510                 //}
42511                 
42512                 this.add(region, ret);
42513                 break;
42514             
42515             /*
42516             case 'TreePanel': // our new panel!
42517                 cfg.el = this.el.createChild();
42518                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42519                 this.add(region, ret);
42520                 break;
42521             */
42522             
42523             case 'Nest': 
42524                 // create a new Layout (which is  a Border Layout...
42525                 
42526                 var clayout = cfg.layout;
42527                 clayout.el  = this.el.createChild();
42528                 clayout.items   = clayout.items  || [];
42529                 
42530                 delete cfg.layout;
42531                 
42532                 // replace this exitems with the clayout ones..
42533                 xitems = clayout.items;
42534                  
42535                 // force background off if it's in center...
42536                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
42537                     cfg.background = false;
42538                 }
42539                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
42540                 
42541                 
42542                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42543                 //console.log('adding nested layout panel '  + cfg.toSource());
42544                 this.add(region, ret);
42545                 nb = {}; /// find first...
42546                 break;
42547             
42548             case 'Grid':
42549                 
42550                 // needs grid and region
42551                 
42552                 //var el = this.getRegion(region).el.createChild();
42553                 /*
42554                  *var el = this.el.createChild();
42555                 // create the grid first...
42556                 cfg.grid.container = el;
42557                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
42558                 */
42559                 
42560                 if (region == 'center' && this.active ) {
42561                     cfg.background = false;
42562                 }
42563                 
42564                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42565                 
42566                 this.add(region, ret);
42567                 /*
42568                 if (cfg.background) {
42569                     // render grid on panel activation (if panel background)
42570                     ret.on('activate', function(gp) {
42571                         if (!gp.grid.rendered) {
42572                     //        gp.grid.render(el);
42573                         }
42574                     });
42575                 } else {
42576                   //  cfg.grid.render(el);
42577                 }
42578                 */
42579                 break;
42580            
42581            
42582             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
42583                 // it was the old xcomponent building that caused this before.
42584                 // espeically if border is the top element in the tree.
42585                 ret = this;
42586                 break; 
42587                 
42588                     
42589                 
42590                 
42591                 
42592             default:
42593                 /*
42594                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
42595                     
42596                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42597                     this.add(region, ret);
42598                 } else {
42599                 */
42600                     Roo.log(cfg);
42601                     throw "Can not add '" + cfg.xtype + "' to Border";
42602                     return null;
42603              
42604                                 
42605              
42606         }
42607         this.beginUpdate();
42608         // add children..
42609         var region = '';
42610         var abn = {};
42611         Roo.each(xitems, function(i)  {
42612             region = nb && i.region ? i.region : false;
42613             
42614             var add = ret.addxtype(i);
42615            
42616             if (region) {
42617                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
42618                 if (!i.background) {
42619                     abn[region] = nb[region] ;
42620                 }
42621             }
42622             
42623         });
42624         this.endUpdate();
42625
42626         // make the last non-background panel active..
42627         //if (nb) { Roo.log(abn); }
42628         if (nb) {
42629             
42630             for(var r in abn) {
42631                 region = this.getRegion(r);
42632                 if (region) {
42633                     // tried using nb[r], but it does not work..
42634                      
42635                     region.showPanel(abn[r]);
42636                    
42637                 }
42638             }
42639         }
42640         return ret;
42641         
42642     },
42643     
42644     
42645 // private
42646     factory : function(cfg)
42647     {
42648         
42649         var validRegions = Roo.bootstrap.layout.Border.regions;
42650
42651         var target = cfg.region;
42652         cfg.mgr = this;
42653         
42654         var r = Roo.bootstrap.layout;
42655         Roo.log(target);
42656         switch(target){
42657             case "north":
42658                 return new r.North(cfg);
42659             case "south":
42660                 return new r.South(cfg);
42661             case "east":
42662                 return new r.East(cfg);
42663             case "west":
42664                 return new r.West(cfg);
42665             case "center":
42666                 return new r.Center(cfg);
42667         }
42668         throw 'Layout region "'+target+'" not supported.';
42669     }
42670     
42671     
42672 });
42673  /*
42674  * Based on:
42675  * Ext JS Library 1.1.1
42676  * Copyright(c) 2006-2007, Ext JS, LLC.
42677  *
42678  * Originally Released Under LGPL - original licence link has changed is not relivant.
42679  *
42680  * Fork - LGPL
42681  * <script type="text/javascript">
42682  */
42683  
42684 /**
42685  * @class Roo.bootstrap.layout.Basic
42686  * @extends Roo.util.Observable
42687  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
42688  * and does not have a titlebar, tabs or any other features. All it does is size and position 
42689  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
42690  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
42691  * @cfg {string}   region  the region that it inhabits..
42692  * @cfg {bool}   skipConfig skip config?
42693  * 
42694
42695  */
42696 Roo.bootstrap.layout.Basic = function(config){
42697     
42698     this.mgr = config.mgr;
42699     
42700     this.position = config.region;
42701     
42702     var skipConfig = config.skipConfig;
42703     
42704     this.events = {
42705         /**
42706          * @scope Roo.BasicLayoutRegion
42707          */
42708         
42709         /**
42710          * @event beforeremove
42711          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
42712          * @param {Roo.LayoutRegion} this
42713          * @param {Roo.ContentPanel} panel The panel
42714          * @param {Object} e The cancel event object
42715          */
42716         "beforeremove" : true,
42717         /**
42718          * @event invalidated
42719          * Fires when the layout for this region is changed.
42720          * @param {Roo.LayoutRegion} this
42721          */
42722         "invalidated" : true,
42723         /**
42724          * @event visibilitychange
42725          * Fires when this region is shown or hidden 
42726          * @param {Roo.LayoutRegion} this
42727          * @param {Boolean} visibility true or false
42728          */
42729         "visibilitychange" : true,
42730         /**
42731          * @event paneladded
42732          * Fires when a panel is added. 
42733          * @param {Roo.LayoutRegion} this
42734          * @param {Roo.ContentPanel} panel The panel
42735          */
42736         "paneladded" : true,
42737         /**
42738          * @event panelremoved
42739          * Fires when a panel is removed. 
42740          * @param {Roo.LayoutRegion} this
42741          * @param {Roo.ContentPanel} panel The panel
42742          */
42743         "panelremoved" : true,
42744         /**
42745          * @event beforecollapse
42746          * Fires when this region before collapse.
42747          * @param {Roo.LayoutRegion} this
42748          */
42749         "beforecollapse" : true,
42750         /**
42751          * @event collapsed
42752          * Fires when this region is collapsed.
42753          * @param {Roo.LayoutRegion} this
42754          */
42755         "collapsed" : true,
42756         /**
42757          * @event expanded
42758          * Fires when this region is expanded.
42759          * @param {Roo.LayoutRegion} this
42760          */
42761         "expanded" : true,
42762         /**
42763          * @event slideshow
42764          * Fires when this region is slid into view.
42765          * @param {Roo.LayoutRegion} this
42766          */
42767         "slideshow" : true,
42768         /**
42769          * @event slidehide
42770          * Fires when this region slides out of view. 
42771          * @param {Roo.LayoutRegion} this
42772          */
42773         "slidehide" : true,
42774         /**
42775          * @event panelactivated
42776          * Fires when a panel is activated. 
42777          * @param {Roo.LayoutRegion} this
42778          * @param {Roo.ContentPanel} panel The activated panel
42779          */
42780         "panelactivated" : true,
42781         /**
42782          * @event resized
42783          * Fires when the user resizes this region. 
42784          * @param {Roo.LayoutRegion} this
42785          * @param {Number} newSize The new size (width for east/west, height for north/south)
42786          */
42787         "resized" : true
42788     };
42789     /** A collection of panels in this region. @type Roo.util.MixedCollection */
42790     this.panels = new Roo.util.MixedCollection();
42791     this.panels.getKey = this.getPanelId.createDelegate(this);
42792     this.box = null;
42793     this.activePanel = null;
42794     // ensure listeners are added...
42795     
42796     if (config.listeners || config.events) {
42797         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
42798             listeners : config.listeners || {},
42799             events : config.events || {}
42800         });
42801     }
42802     
42803     if(skipConfig !== true){
42804         this.applyConfig(config);
42805     }
42806 };
42807
42808 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
42809 {
42810     getPanelId : function(p){
42811         return p.getId();
42812     },
42813     
42814     applyConfig : function(config){
42815         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
42816         this.config = config;
42817         
42818     },
42819     
42820     /**
42821      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
42822      * the width, for horizontal (north, south) the height.
42823      * @param {Number} newSize The new width or height
42824      */
42825     resizeTo : function(newSize){
42826         var el = this.el ? this.el :
42827                  (this.activePanel ? this.activePanel.getEl() : null);
42828         if(el){
42829             switch(this.position){
42830                 case "east":
42831                 case "west":
42832                     el.setWidth(newSize);
42833                     this.fireEvent("resized", this, newSize);
42834                 break;
42835                 case "north":
42836                 case "south":
42837                     el.setHeight(newSize);
42838                     this.fireEvent("resized", this, newSize);
42839                 break;                
42840             }
42841         }
42842     },
42843     
42844     getBox : function(){
42845         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
42846     },
42847     
42848     getMargins : function(){
42849         return this.margins;
42850     },
42851     
42852     updateBox : function(box){
42853         this.box = box;
42854         var el = this.activePanel.getEl();
42855         el.dom.style.left = box.x + "px";
42856         el.dom.style.top = box.y + "px";
42857         this.activePanel.setSize(box.width, box.height);
42858     },
42859     
42860     /**
42861      * Returns the container element for this region.
42862      * @return {Roo.Element}
42863      */
42864     getEl : function(){
42865         return this.activePanel;
42866     },
42867     
42868     /**
42869      * Returns true if this region is currently visible.
42870      * @return {Boolean}
42871      */
42872     isVisible : function(){
42873         return this.activePanel ? true : false;
42874     },
42875     
42876     setActivePanel : function(panel){
42877         panel = this.getPanel(panel);
42878         if(this.activePanel && this.activePanel != panel){
42879             this.activePanel.setActiveState(false);
42880             this.activePanel.getEl().setLeftTop(-10000,-10000);
42881         }
42882         this.activePanel = panel;
42883         panel.setActiveState(true);
42884         if(this.box){
42885             panel.setSize(this.box.width, this.box.height);
42886         }
42887         this.fireEvent("panelactivated", this, panel);
42888         this.fireEvent("invalidated");
42889     },
42890     
42891     /**
42892      * Show the specified panel.
42893      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
42894      * @return {Roo.ContentPanel} The shown panel or null
42895      */
42896     showPanel : function(panel){
42897         panel = this.getPanel(panel);
42898         if(panel){
42899             this.setActivePanel(panel);
42900         }
42901         return panel;
42902     },
42903     
42904     /**
42905      * Get the active panel for this region.
42906      * @return {Roo.ContentPanel} The active panel or null
42907      */
42908     getActivePanel : function(){
42909         return this.activePanel;
42910     },
42911     
42912     /**
42913      * Add the passed ContentPanel(s)
42914      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42915      * @return {Roo.ContentPanel} The panel added (if only one was added)
42916      */
42917     add : function(panel){
42918         if(arguments.length > 1){
42919             for(var i = 0, len = arguments.length; i < len; i++) {
42920                 this.add(arguments[i]);
42921             }
42922             return null;
42923         }
42924         if(this.hasPanel(panel)){
42925             this.showPanel(panel);
42926             return panel;
42927         }
42928         var el = panel.getEl();
42929         if(el.dom.parentNode != this.mgr.el.dom){
42930             this.mgr.el.dom.appendChild(el.dom);
42931         }
42932         if(panel.setRegion){
42933             panel.setRegion(this);
42934         }
42935         this.panels.add(panel);
42936         el.setStyle("position", "absolute");
42937         if(!panel.background){
42938             this.setActivePanel(panel);
42939             if(this.config.initialSize && this.panels.getCount()==1){
42940                 this.resizeTo(this.config.initialSize);
42941             }
42942         }
42943         this.fireEvent("paneladded", this, panel);
42944         return panel;
42945     },
42946     
42947     /**
42948      * Returns true if the panel is in this region.
42949      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
42950      * @return {Boolean}
42951      */
42952     hasPanel : function(panel){
42953         if(typeof panel == "object"){ // must be panel obj
42954             panel = panel.getId();
42955         }
42956         return this.getPanel(panel) ? true : false;
42957     },
42958     
42959     /**
42960      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
42961      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
42962      * @param {Boolean} preservePanel Overrides the config preservePanel option
42963      * @return {Roo.ContentPanel} The panel that was removed
42964      */
42965     remove : function(panel, preservePanel){
42966         panel = this.getPanel(panel);
42967         if(!panel){
42968             return null;
42969         }
42970         var e = {};
42971         this.fireEvent("beforeremove", this, panel, e);
42972         if(e.cancel === true){
42973             return null;
42974         }
42975         var panelId = panel.getId();
42976         this.panels.removeKey(panelId);
42977         return panel;
42978     },
42979     
42980     /**
42981      * Returns the panel specified or null if it's not in this region.
42982      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
42983      * @return {Roo.ContentPanel}
42984      */
42985     getPanel : function(id){
42986         if(typeof id == "object"){ // must be panel obj
42987             return id;
42988         }
42989         return this.panels.get(id);
42990     },
42991     
42992     /**
42993      * Returns this regions position (north/south/east/west/center).
42994      * @return {String} 
42995      */
42996     getPosition: function(){
42997         return this.position;    
42998     }
42999 });/*
43000  * Based on:
43001  * Ext JS Library 1.1.1
43002  * Copyright(c) 2006-2007, Ext JS, LLC.
43003  *
43004  * Originally Released Under LGPL - original licence link has changed is not relivant.
43005  *
43006  * Fork - LGPL
43007  * <script type="text/javascript">
43008  */
43009  
43010 /**
43011  * @class Roo.bootstrap.layout.Region
43012  * @extends Roo.bootstrap.layout.Basic
43013  * This class represents a region in a layout manager.
43014  
43015  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43016  * @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})
43017  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
43018  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
43019  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
43020  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
43021  * @cfg {String}    title           The title for the region (overrides panel titles)
43022  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
43023  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43024  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
43025  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43026  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
43027  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43028  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
43029  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
43030  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
43031  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
43032
43033  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
43034  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
43035  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
43036  * @cfg {Number}    width           For East/West panels
43037  * @cfg {Number}    height          For North/South panels
43038  * @cfg {Boolean}   split           To show the splitter
43039  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
43040  * 
43041  * @cfg {string}   cls             Extra CSS classes to add to region
43042  * 
43043  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43044  * @cfg {string}   region  the region that it inhabits..
43045  *
43046
43047  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
43048  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
43049
43050  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
43051  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
43052  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
43053  */
43054 Roo.bootstrap.layout.Region = function(config)
43055 {
43056     this.applyConfig(config);
43057
43058     var mgr = config.mgr;
43059     var pos = config.region;
43060     config.skipConfig = true;
43061     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43062     
43063     if (mgr.el) {
43064         this.onRender(mgr.el);   
43065     }
43066      
43067     this.visible = true;
43068     this.collapsed = false;
43069     this.unrendered_panels = [];
43070 };
43071
43072 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43073
43074     position: '', // set by wrapper (eg. north/south etc..)
43075     unrendered_panels : null,  // unrendered panels.
43076     
43077     tabPosition : false,
43078     
43079     mgr: false, // points to 'Border'
43080     
43081     
43082     createBody : function(){
43083         /** This region's body element 
43084         * @type Roo.Element */
43085         this.bodyEl = this.el.createChild({
43086                 tag: "div",
43087                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43088         });
43089     },
43090
43091     onRender: function(ctr, pos)
43092     {
43093         var dh = Roo.DomHelper;
43094         /** This region's container element 
43095         * @type Roo.Element */
43096         this.el = dh.append(ctr.dom, {
43097                 tag: "div",
43098                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43099             }, true);
43100         /** This region's title element 
43101         * @type Roo.Element */
43102     
43103         this.titleEl = dh.append(this.el.dom,  {
43104                 tag: "div",
43105                 unselectable: "on",
43106                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43107                 children:[
43108                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43109                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43110                 ]
43111             }, true);
43112         
43113         this.titleEl.enableDisplayMode();
43114         /** This region's title text element 
43115         * @type HTMLElement */
43116         this.titleTextEl = this.titleEl.dom.firstChild;
43117         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43118         /*
43119         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43120         this.closeBtn.enableDisplayMode();
43121         this.closeBtn.on("click", this.closeClicked, this);
43122         this.closeBtn.hide();
43123     */
43124         this.createBody(this.config);
43125         if(this.config.hideWhenEmpty){
43126             this.hide();
43127             this.on("paneladded", this.validateVisibility, this);
43128             this.on("panelremoved", this.validateVisibility, this);
43129         }
43130         if(this.autoScroll){
43131             this.bodyEl.setStyle("overflow", "auto");
43132         }else{
43133             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
43134         }
43135         //if(c.titlebar !== false){
43136             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
43137                 this.titleEl.hide();
43138             }else{
43139                 this.titleEl.show();
43140                 if(this.config.title){
43141                     this.titleTextEl.innerHTML = this.config.title;
43142                 }
43143             }
43144         //}
43145         if(this.config.collapsed){
43146             this.collapse(true);
43147         }
43148         if(this.config.hidden){
43149             this.hide();
43150         }
43151         
43152         if (this.unrendered_panels && this.unrendered_panels.length) {
43153             for (var i =0;i< this.unrendered_panels.length; i++) {
43154                 this.add(this.unrendered_panels[i]);
43155             }
43156             this.unrendered_panels = null;
43157             
43158         }
43159         
43160     },
43161     
43162     applyConfig : function(c)
43163     {
43164         /*
43165          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
43166             var dh = Roo.DomHelper;
43167             if(c.titlebar !== false){
43168                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
43169                 this.collapseBtn.on("click", this.collapse, this);
43170                 this.collapseBtn.enableDisplayMode();
43171                 /*
43172                 if(c.showPin === true || this.showPin){
43173                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
43174                     this.stickBtn.enableDisplayMode();
43175                     this.stickBtn.on("click", this.expand, this);
43176                     this.stickBtn.hide();
43177                 }
43178                 
43179             }
43180             */
43181             /** This region's collapsed element
43182             * @type Roo.Element */
43183             /*
43184              *
43185             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43186                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43187             ]}, true);
43188             
43189             if(c.floatable !== false){
43190                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43191                this.collapsedEl.on("click", this.collapseClick, this);
43192             }
43193
43194             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43195                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43196                    id: "message", unselectable: "on", style:{"float":"left"}});
43197                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43198              }
43199             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43200             this.expandBtn.on("click", this.expand, this);
43201             
43202         }
43203         
43204         if(this.collapseBtn){
43205             this.collapseBtn.setVisible(c.collapsible == true);
43206         }
43207         
43208         this.cmargins = c.cmargins || this.cmargins ||
43209                          (this.position == "west" || this.position == "east" ?
43210                              {top: 0, left: 2, right:2, bottom: 0} :
43211                              {top: 2, left: 0, right:0, bottom: 2});
43212         */
43213         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43214         
43215         
43216         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
43217         
43218         this.autoScroll = c.autoScroll || false;
43219         
43220         
43221        
43222         
43223         this.duration = c.duration || .30;
43224         this.slideDuration = c.slideDuration || .45;
43225         this.config = c;
43226        
43227     },
43228     /**
43229      * Returns true if this region is currently visible.
43230      * @return {Boolean}
43231      */
43232     isVisible : function(){
43233         return this.visible;
43234     },
43235
43236     /**
43237      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43238      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
43239      */
43240     //setCollapsedTitle : function(title){
43241     //    title = title || "&#160;";
43242      //   if(this.collapsedTitleTextEl){
43243       //      this.collapsedTitleTextEl.innerHTML = title;
43244        // }
43245     //},
43246
43247     getBox : function(){
43248         var b;
43249       //  if(!this.collapsed){
43250             b = this.el.getBox(false, true);
43251        // }else{
43252           //  b = this.collapsedEl.getBox(false, true);
43253         //}
43254         return b;
43255     },
43256
43257     getMargins : function(){
43258         return this.margins;
43259         //return this.collapsed ? this.cmargins : this.margins;
43260     },
43261 /*
43262     highlight : function(){
43263         this.el.addClass("x-layout-panel-dragover");
43264     },
43265
43266     unhighlight : function(){
43267         this.el.removeClass("x-layout-panel-dragover");
43268     },
43269 */
43270     updateBox : function(box)
43271     {
43272         if (!this.bodyEl) {
43273             return; // not rendered yet..
43274         }
43275         
43276         this.box = box;
43277         if(!this.collapsed){
43278             this.el.dom.style.left = box.x + "px";
43279             this.el.dom.style.top = box.y + "px";
43280             this.updateBody(box.width, box.height);
43281         }else{
43282             this.collapsedEl.dom.style.left = box.x + "px";
43283             this.collapsedEl.dom.style.top = box.y + "px";
43284             this.collapsedEl.setSize(box.width, box.height);
43285         }
43286         if(this.tabs){
43287             this.tabs.autoSizeTabs();
43288         }
43289     },
43290
43291     updateBody : function(w, h)
43292     {
43293         if(w !== null){
43294             this.el.setWidth(w);
43295             w -= this.el.getBorderWidth("rl");
43296             if(this.config.adjustments){
43297                 w += this.config.adjustments[0];
43298             }
43299         }
43300         if(h !== null && h > 0){
43301             this.el.setHeight(h);
43302             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43303             h -= this.el.getBorderWidth("tb");
43304             if(this.config.adjustments){
43305                 h += this.config.adjustments[1];
43306             }
43307             this.bodyEl.setHeight(h);
43308             if(this.tabs){
43309                 h = this.tabs.syncHeight(h);
43310             }
43311         }
43312         if(this.panelSize){
43313             w = w !== null ? w : this.panelSize.width;
43314             h = h !== null ? h : this.panelSize.height;
43315         }
43316         if(this.activePanel){
43317             var el = this.activePanel.getEl();
43318             w = w !== null ? w : el.getWidth();
43319             h = h !== null ? h : el.getHeight();
43320             this.panelSize = {width: w, height: h};
43321             this.activePanel.setSize(w, h);
43322         }
43323         if(Roo.isIE && this.tabs){
43324             this.tabs.el.repaint();
43325         }
43326     },
43327
43328     /**
43329      * Returns the container element for this region.
43330      * @return {Roo.Element}
43331      */
43332     getEl : function(){
43333         return this.el;
43334     },
43335
43336     /**
43337      * Hides this region.
43338      */
43339     hide : function(){
43340         //if(!this.collapsed){
43341             this.el.dom.style.left = "-2000px";
43342             this.el.hide();
43343         //}else{
43344          //   this.collapsedEl.dom.style.left = "-2000px";
43345          //   this.collapsedEl.hide();
43346        // }
43347         this.visible = false;
43348         this.fireEvent("visibilitychange", this, false);
43349     },
43350
43351     /**
43352      * Shows this region if it was previously hidden.
43353      */
43354     show : function(){
43355         //if(!this.collapsed){
43356             this.el.show();
43357         //}else{
43358         //    this.collapsedEl.show();
43359        // }
43360         this.visible = true;
43361         this.fireEvent("visibilitychange", this, true);
43362     },
43363 /*
43364     closeClicked : function(){
43365         if(this.activePanel){
43366             this.remove(this.activePanel);
43367         }
43368     },
43369
43370     collapseClick : function(e){
43371         if(this.isSlid){
43372            e.stopPropagation();
43373            this.slideIn();
43374         }else{
43375            e.stopPropagation();
43376            this.slideOut();
43377         }
43378     },
43379 */
43380     /**
43381      * Collapses this region.
43382      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43383      */
43384     /*
43385     collapse : function(skipAnim, skipCheck = false){
43386         if(this.collapsed) {
43387             return;
43388         }
43389         
43390         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
43391             
43392             this.collapsed = true;
43393             if(this.split){
43394                 this.split.el.hide();
43395             }
43396             if(this.config.animate && skipAnim !== true){
43397                 this.fireEvent("invalidated", this);
43398                 this.animateCollapse();
43399             }else{
43400                 this.el.setLocation(-20000,-20000);
43401                 this.el.hide();
43402                 this.collapsedEl.show();
43403                 this.fireEvent("collapsed", this);
43404                 this.fireEvent("invalidated", this);
43405             }
43406         }
43407         
43408     },
43409 */
43410     animateCollapse : function(){
43411         // overridden
43412     },
43413
43414     /**
43415      * Expands this region if it was previously collapsed.
43416      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43417      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43418      */
43419     /*
43420     expand : function(e, skipAnim){
43421         if(e) {
43422             e.stopPropagation();
43423         }
43424         if(!this.collapsed || this.el.hasActiveFx()) {
43425             return;
43426         }
43427         if(this.isSlid){
43428             this.afterSlideIn();
43429             skipAnim = true;
43430         }
43431         this.collapsed = false;
43432         if(this.config.animate && skipAnim !== true){
43433             this.animateExpand();
43434         }else{
43435             this.el.show();
43436             if(this.split){
43437                 this.split.el.show();
43438             }
43439             this.collapsedEl.setLocation(-2000,-2000);
43440             this.collapsedEl.hide();
43441             this.fireEvent("invalidated", this);
43442             this.fireEvent("expanded", this);
43443         }
43444     },
43445 */
43446     animateExpand : function(){
43447         // overridden
43448     },
43449
43450     initTabs : function()
43451     {
43452         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
43453         
43454         var ts = new Roo.bootstrap.panel.Tabs({
43455             el: this.bodyEl.dom,
43456             region : this,
43457             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
43458             disableTooltips: this.config.disableTabTips,
43459             toolbar : this.config.toolbar
43460         });
43461         
43462         if(this.config.hideTabs){
43463             ts.stripWrap.setDisplayed(false);
43464         }
43465         this.tabs = ts;
43466         ts.resizeTabs = this.config.resizeTabs === true;
43467         ts.minTabWidth = this.config.minTabWidth || 40;
43468         ts.maxTabWidth = this.config.maxTabWidth || 250;
43469         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
43470         ts.monitorResize = false;
43471         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
43472         ts.bodyEl.addClass('roo-layout-tabs-body');
43473         this.panels.each(this.initPanelAsTab, this);
43474     },
43475
43476     initPanelAsTab : function(panel){
43477         var ti = this.tabs.addTab(
43478             panel.getEl().id,
43479             panel.getTitle(),
43480             null,
43481             this.config.closeOnTab && panel.isClosable(),
43482             panel.tpl
43483         );
43484         if(panel.tabTip !== undefined){
43485             ti.setTooltip(panel.tabTip);
43486         }
43487         ti.on("activate", function(){
43488               this.setActivePanel(panel);
43489         }, this);
43490         
43491         if(this.config.closeOnTab){
43492             ti.on("beforeclose", function(t, e){
43493                 e.cancel = true;
43494                 this.remove(panel);
43495             }, this);
43496         }
43497         
43498         panel.tabItem = ti;
43499         
43500         return ti;
43501     },
43502
43503     updatePanelTitle : function(panel, title)
43504     {
43505         if(this.activePanel == panel){
43506             this.updateTitle(title);
43507         }
43508         if(this.tabs){
43509             var ti = this.tabs.getTab(panel.getEl().id);
43510             ti.setText(title);
43511             if(panel.tabTip !== undefined){
43512                 ti.setTooltip(panel.tabTip);
43513             }
43514         }
43515     },
43516
43517     updateTitle : function(title){
43518         if(this.titleTextEl && !this.config.title){
43519             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
43520         }
43521     },
43522
43523     setActivePanel : function(panel)
43524     {
43525         panel = this.getPanel(panel);
43526         if(this.activePanel && this.activePanel != panel){
43527             if(this.activePanel.setActiveState(false) === false){
43528                 return;
43529             }
43530         }
43531         this.activePanel = panel;
43532         panel.setActiveState(true);
43533         if(this.panelSize){
43534             panel.setSize(this.panelSize.width, this.panelSize.height);
43535         }
43536         if(this.closeBtn){
43537             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
43538         }
43539         this.updateTitle(panel.getTitle());
43540         if(this.tabs){
43541             this.fireEvent("invalidated", this);
43542         }
43543         this.fireEvent("panelactivated", this, panel);
43544     },
43545
43546     /**
43547      * Shows the specified panel.
43548      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
43549      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
43550      */
43551     showPanel : function(panel)
43552     {
43553         panel = this.getPanel(panel);
43554         if(panel){
43555             if(this.tabs){
43556                 var tab = this.tabs.getTab(panel.getEl().id);
43557                 if(tab.isHidden()){
43558                     this.tabs.unhideTab(tab.id);
43559                 }
43560                 tab.activate();
43561             }else{
43562                 this.setActivePanel(panel);
43563             }
43564         }
43565         return panel;
43566     },
43567
43568     /**
43569      * Get the active panel for this region.
43570      * @return {Roo.ContentPanel} The active panel or null
43571      */
43572     getActivePanel : function(){
43573         return this.activePanel;
43574     },
43575
43576     validateVisibility : function(){
43577         if(this.panels.getCount() < 1){
43578             this.updateTitle("&#160;");
43579             this.closeBtn.hide();
43580             this.hide();
43581         }else{
43582             if(!this.isVisible()){
43583                 this.show();
43584             }
43585         }
43586     },
43587
43588     /**
43589      * Adds the passed ContentPanel(s) to this region.
43590      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43591      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
43592      */
43593     add : function(panel)
43594     {
43595         if(arguments.length > 1){
43596             for(var i = 0, len = arguments.length; i < len; i++) {
43597                 this.add(arguments[i]);
43598             }
43599             return null;
43600         }
43601         
43602         // if we have not been rendered yet, then we can not really do much of this..
43603         if (!this.bodyEl) {
43604             this.unrendered_panels.push(panel);
43605             return panel;
43606         }
43607         
43608         
43609         
43610         
43611         if(this.hasPanel(panel)){
43612             this.showPanel(panel);
43613             return panel;
43614         }
43615         panel.setRegion(this);
43616         this.panels.add(panel);
43617        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
43618             // sinle panel - no tab...?? would it not be better to render it with the tabs,
43619             // and hide them... ???
43620             this.bodyEl.dom.appendChild(panel.getEl().dom);
43621             if(panel.background !== true){
43622                 this.setActivePanel(panel);
43623             }
43624             this.fireEvent("paneladded", this, panel);
43625             return panel;
43626         }
43627         */
43628         if(!this.tabs){
43629             this.initTabs();
43630         }else{
43631             this.initPanelAsTab(panel);
43632         }
43633         
43634         
43635         if(panel.background !== true){
43636             this.tabs.activate(panel.getEl().id);
43637         }
43638         this.fireEvent("paneladded", this, panel);
43639         return panel;
43640     },
43641
43642     /**
43643      * Hides the tab for the specified panel.
43644      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43645      */
43646     hidePanel : function(panel){
43647         if(this.tabs && (panel = this.getPanel(panel))){
43648             this.tabs.hideTab(panel.getEl().id);
43649         }
43650     },
43651
43652     /**
43653      * Unhides the tab for a previously hidden panel.
43654      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43655      */
43656     unhidePanel : function(panel){
43657         if(this.tabs && (panel = this.getPanel(panel))){
43658             this.tabs.unhideTab(panel.getEl().id);
43659         }
43660     },
43661
43662     clearPanels : function(){
43663         while(this.panels.getCount() > 0){
43664              this.remove(this.panels.first());
43665         }
43666     },
43667
43668     /**
43669      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43670      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43671      * @param {Boolean} preservePanel Overrides the config preservePanel option
43672      * @return {Roo.ContentPanel} The panel that was removed
43673      */
43674     remove : function(panel, preservePanel)
43675     {
43676         panel = this.getPanel(panel);
43677         if(!panel){
43678             return null;
43679         }
43680         var e = {};
43681         this.fireEvent("beforeremove", this, panel, e);
43682         if(e.cancel === true){
43683             return null;
43684         }
43685         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
43686         var panelId = panel.getId();
43687         this.panels.removeKey(panelId);
43688         if(preservePanel){
43689             document.body.appendChild(panel.getEl().dom);
43690         }
43691         if(this.tabs){
43692             this.tabs.removeTab(panel.getEl().id);
43693         }else if (!preservePanel){
43694             this.bodyEl.dom.removeChild(panel.getEl().dom);
43695         }
43696         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
43697             var p = this.panels.first();
43698             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
43699             tempEl.appendChild(p.getEl().dom);
43700             this.bodyEl.update("");
43701             this.bodyEl.dom.appendChild(p.getEl().dom);
43702             tempEl = null;
43703             this.updateTitle(p.getTitle());
43704             this.tabs = null;
43705             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43706             this.setActivePanel(p);
43707         }
43708         panel.setRegion(null);
43709         if(this.activePanel == panel){
43710             this.activePanel = null;
43711         }
43712         if(this.config.autoDestroy !== false && preservePanel !== true){
43713             try{panel.destroy();}catch(e){}
43714         }
43715         this.fireEvent("panelremoved", this, panel);
43716         return panel;
43717     },
43718
43719     /**
43720      * Returns the TabPanel component used by this region
43721      * @return {Roo.TabPanel}
43722      */
43723     getTabs : function(){
43724         return this.tabs;
43725     },
43726
43727     createTool : function(parentEl, className){
43728         var btn = Roo.DomHelper.append(parentEl, {
43729             tag: "div",
43730             cls: "x-layout-tools-button",
43731             children: [ {
43732                 tag: "div",
43733                 cls: "roo-layout-tools-button-inner " + className,
43734                 html: "&#160;"
43735             }]
43736         }, true);
43737         btn.addClassOnOver("roo-layout-tools-button-over");
43738         return btn;
43739     }
43740 });/*
43741  * Based on:
43742  * Ext JS Library 1.1.1
43743  * Copyright(c) 2006-2007, Ext JS, LLC.
43744  *
43745  * Originally Released Under LGPL - original licence link has changed is not relivant.
43746  *
43747  * Fork - LGPL
43748  * <script type="text/javascript">
43749  */
43750  
43751
43752
43753 /**
43754  * @class Roo.SplitLayoutRegion
43755  * @extends Roo.LayoutRegion
43756  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
43757  */
43758 Roo.bootstrap.layout.Split = function(config){
43759     this.cursor = config.cursor;
43760     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
43761 };
43762
43763 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
43764 {
43765     splitTip : "Drag to resize.",
43766     collapsibleSplitTip : "Drag to resize. Double click to hide.",
43767     useSplitTips : false,
43768
43769     applyConfig : function(config){
43770         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
43771     },
43772     
43773     onRender : function(ctr,pos) {
43774         
43775         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
43776         if(!this.config.split){
43777             return;
43778         }
43779         if(!this.split){
43780             
43781             var splitEl = Roo.DomHelper.append(ctr.dom,  {
43782                             tag: "div",
43783                             id: this.el.id + "-split",
43784                             cls: "roo-layout-split roo-layout-split-"+this.position,
43785                             html: "&#160;"
43786             });
43787             /** The SplitBar for this region 
43788             * @type Roo.SplitBar */
43789             // does not exist yet...
43790             Roo.log([this.position, this.orientation]);
43791             
43792             this.split = new Roo.bootstrap.SplitBar({
43793                 dragElement : splitEl,
43794                 resizingElement: this.el,
43795                 orientation : this.orientation
43796             });
43797             
43798             this.split.on("moved", this.onSplitMove, this);
43799             this.split.useShim = this.config.useShim === true;
43800             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
43801             if(this.useSplitTips){
43802                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
43803             }
43804             //if(config.collapsible){
43805             //    this.split.el.on("dblclick", this.collapse,  this);
43806             //}
43807         }
43808         if(typeof this.config.minSize != "undefined"){
43809             this.split.minSize = this.config.minSize;
43810         }
43811         if(typeof this.config.maxSize != "undefined"){
43812             this.split.maxSize = this.config.maxSize;
43813         }
43814         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
43815             this.hideSplitter();
43816         }
43817         
43818     },
43819
43820     getHMaxSize : function(){
43821          var cmax = this.config.maxSize || 10000;
43822          var center = this.mgr.getRegion("center");
43823          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
43824     },
43825
43826     getVMaxSize : function(){
43827          var cmax = this.config.maxSize || 10000;
43828          var center = this.mgr.getRegion("center");
43829          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
43830     },
43831
43832     onSplitMove : function(split, newSize){
43833         this.fireEvent("resized", this, newSize);
43834     },
43835     
43836     /** 
43837      * Returns the {@link Roo.SplitBar} for this region.
43838      * @return {Roo.SplitBar}
43839      */
43840     getSplitBar : function(){
43841         return this.split;
43842     },
43843     
43844     hide : function(){
43845         this.hideSplitter();
43846         Roo.bootstrap.layout.Split.superclass.hide.call(this);
43847     },
43848
43849     hideSplitter : function(){
43850         if(this.split){
43851             this.split.el.setLocation(-2000,-2000);
43852             this.split.el.hide();
43853         }
43854     },
43855
43856     show : function(){
43857         if(this.split){
43858             this.split.el.show();
43859         }
43860         Roo.bootstrap.layout.Split.superclass.show.call(this);
43861     },
43862     
43863     beforeSlide: function(){
43864         if(Roo.isGecko){// firefox overflow auto bug workaround
43865             this.bodyEl.clip();
43866             if(this.tabs) {
43867                 this.tabs.bodyEl.clip();
43868             }
43869             if(this.activePanel){
43870                 this.activePanel.getEl().clip();
43871                 
43872                 if(this.activePanel.beforeSlide){
43873                     this.activePanel.beforeSlide();
43874                 }
43875             }
43876         }
43877     },
43878     
43879     afterSlide : function(){
43880         if(Roo.isGecko){// firefox overflow auto bug workaround
43881             this.bodyEl.unclip();
43882             if(this.tabs) {
43883                 this.tabs.bodyEl.unclip();
43884             }
43885             if(this.activePanel){
43886                 this.activePanel.getEl().unclip();
43887                 if(this.activePanel.afterSlide){
43888                     this.activePanel.afterSlide();
43889                 }
43890             }
43891         }
43892     },
43893
43894     initAutoHide : function(){
43895         if(this.autoHide !== false){
43896             if(!this.autoHideHd){
43897                 var st = new Roo.util.DelayedTask(this.slideIn, this);
43898                 this.autoHideHd = {
43899                     "mouseout": function(e){
43900                         if(!e.within(this.el, true)){
43901                             st.delay(500);
43902                         }
43903                     },
43904                     "mouseover" : function(e){
43905                         st.cancel();
43906                     },
43907                     scope : this
43908                 };
43909             }
43910             this.el.on(this.autoHideHd);
43911         }
43912     },
43913
43914     clearAutoHide : function(){
43915         if(this.autoHide !== false){
43916             this.el.un("mouseout", this.autoHideHd.mouseout);
43917             this.el.un("mouseover", this.autoHideHd.mouseover);
43918         }
43919     },
43920
43921     clearMonitor : function(){
43922         Roo.get(document).un("click", this.slideInIf, this);
43923     },
43924
43925     // these names are backwards but not changed for compat
43926     slideOut : function(){
43927         if(this.isSlid || this.el.hasActiveFx()){
43928             return;
43929         }
43930         this.isSlid = true;
43931         if(this.collapseBtn){
43932             this.collapseBtn.hide();
43933         }
43934         this.closeBtnState = this.closeBtn.getStyle('display');
43935         this.closeBtn.hide();
43936         if(this.stickBtn){
43937             this.stickBtn.show();
43938         }
43939         this.el.show();
43940         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
43941         this.beforeSlide();
43942         this.el.setStyle("z-index", 10001);
43943         this.el.slideIn(this.getSlideAnchor(), {
43944             callback: function(){
43945                 this.afterSlide();
43946                 this.initAutoHide();
43947                 Roo.get(document).on("click", this.slideInIf, this);
43948                 this.fireEvent("slideshow", this);
43949             },
43950             scope: this,
43951             block: true
43952         });
43953     },
43954
43955     afterSlideIn : function(){
43956         this.clearAutoHide();
43957         this.isSlid = false;
43958         this.clearMonitor();
43959         this.el.setStyle("z-index", "");
43960         if(this.collapseBtn){
43961             this.collapseBtn.show();
43962         }
43963         this.closeBtn.setStyle('display', this.closeBtnState);
43964         if(this.stickBtn){
43965             this.stickBtn.hide();
43966         }
43967         this.fireEvent("slidehide", this);
43968     },
43969
43970     slideIn : function(cb){
43971         if(!this.isSlid || this.el.hasActiveFx()){
43972             Roo.callback(cb);
43973             return;
43974         }
43975         this.isSlid = false;
43976         this.beforeSlide();
43977         this.el.slideOut(this.getSlideAnchor(), {
43978             callback: function(){
43979                 this.el.setLeftTop(-10000, -10000);
43980                 this.afterSlide();
43981                 this.afterSlideIn();
43982                 Roo.callback(cb);
43983             },
43984             scope: this,
43985             block: true
43986         });
43987     },
43988     
43989     slideInIf : function(e){
43990         if(!e.within(this.el)){
43991             this.slideIn();
43992         }
43993     },
43994
43995     animateCollapse : function(){
43996         this.beforeSlide();
43997         this.el.setStyle("z-index", 20000);
43998         var anchor = this.getSlideAnchor();
43999         this.el.slideOut(anchor, {
44000             callback : function(){
44001                 this.el.setStyle("z-index", "");
44002                 this.collapsedEl.slideIn(anchor, {duration:.3});
44003                 this.afterSlide();
44004                 this.el.setLocation(-10000,-10000);
44005                 this.el.hide();
44006                 this.fireEvent("collapsed", this);
44007             },
44008             scope: this,
44009             block: true
44010         });
44011     },
44012
44013     animateExpand : function(){
44014         this.beforeSlide();
44015         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44016         this.el.setStyle("z-index", 20000);
44017         this.collapsedEl.hide({
44018             duration:.1
44019         });
44020         this.el.slideIn(this.getSlideAnchor(), {
44021             callback : function(){
44022                 this.el.setStyle("z-index", "");
44023                 this.afterSlide();
44024                 if(this.split){
44025                     this.split.el.show();
44026                 }
44027                 this.fireEvent("invalidated", this);
44028                 this.fireEvent("expanded", this);
44029             },
44030             scope: this,
44031             block: true
44032         });
44033     },
44034
44035     anchors : {
44036         "west" : "left",
44037         "east" : "right",
44038         "north" : "top",
44039         "south" : "bottom"
44040     },
44041
44042     sanchors : {
44043         "west" : "l",
44044         "east" : "r",
44045         "north" : "t",
44046         "south" : "b"
44047     },
44048
44049     canchors : {
44050         "west" : "tl-tr",
44051         "east" : "tr-tl",
44052         "north" : "tl-bl",
44053         "south" : "bl-tl"
44054     },
44055
44056     getAnchor : function(){
44057         return this.anchors[this.position];
44058     },
44059
44060     getCollapseAnchor : function(){
44061         return this.canchors[this.position];
44062     },
44063
44064     getSlideAnchor : function(){
44065         return this.sanchors[this.position];
44066     },
44067
44068     getAlignAdj : function(){
44069         var cm = this.cmargins;
44070         switch(this.position){
44071             case "west":
44072                 return [0, 0];
44073             break;
44074             case "east":
44075                 return [0, 0];
44076             break;
44077             case "north":
44078                 return [0, 0];
44079             break;
44080             case "south":
44081                 return [0, 0];
44082             break;
44083         }
44084     },
44085
44086     getExpandAdj : function(){
44087         var c = this.collapsedEl, cm = this.cmargins;
44088         switch(this.position){
44089             case "west":
44090                 return [-(cm.right+c.getWidth()+cm.left), 0];
44091             break;
44092             case "east":
44093                 return [cm.right+c.getWidth()+cm.left, 0];
44094             break;
44095             case "north":
44096                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44097             break;
44098             case "south":
44099                 return [0, cm.top+cm.bottom+c.getHeight()];
44100             break;
44101         }
44102     }
44103 });/*
44104  * Based on:
44105  * Ext JS Library 1.1.1
44106  * Copyright(c) 2006-2007, Ext JS, LLC.
44107  *
44108  * Originally Released Under LGPL - original licence link has changed is not relivant.
44109  *
44110  * Fork - LGPL
44111  * <script type="text/javascript">
44112  */
44113 /*
44114  * These classes are private internal classes
44115  */
44116 Roo.bootstrap.layout.Center = function(config){
44117     config.region = "center";
44118     Roo.bootstrap.layout.Region.call(this, config);
44119     this.visible = true;
44120     this.minWidth = config.minWidth || 20;
44121     this.minHeight = config.minHeight || 20;
44122 };
44123
44124 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
44125     hide : function(){
44126         // center panel can't be hidden
44127     },
44128     
44129     show : function(){
44130         // center panel can't be hidden
44131     },
44132     
44133     getMinWidth: function(){
44134         return this.minWidth;
44135     },
44136     
44137     getMinHeight: function(){
44138         return this.minHeight;
44139     }
44140 });
44141
44142
44143
44144
44145  
44146
44147
44148
44149
44150
44151
44152 Roo.bootstrap.layout.North = function(config)
44153 {
44154     config.region = 'north';
44155     config.cursor = 'n-resize';
44156     
44157     Roo.bootstrap.layout.Split.call(this, config);
44158     
44159     
44160     if(this.split){
44161         this.split.placement = Roo.bootstrap.SplitBar.TOP;
44162         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44163         this.split.el.addClass("roo-layout-split-v");
44164     }
44165     //var size = config.initialSize || config.height;
44166     //if(this.el && typeof size != "undefined"){
44167     //    this.el.setHeight(size);
44168     //}
44169 };
44170 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
44171 {
44172     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44173      
44174      
44175     onRender : function(ctr, pos)
44176     {
44177         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44178         var size = this.config.initialSize || this.config.height;
44179         if(this.el && typeof size != "undefined"){
44180             this.el.setHeight(size);
44181         }
44182     
44183     },
44184     
44185     getBox : function(){
44186         if(this.collapsed){
44187             return this.collapsedEl.getBox();
44188         }
44189         var box = this.el.getBox();
44190         if(this.split){
44191             box.height += this.split.el.getHeight();
44192         }
44193         return box;
44194     },
44195     
44196     updateBox : function(box){
44197         if(this.split && !this.collapsed){
44198             box.height -= this.split.el.getHeight();
44199             this.split.el.setLeft(box.x);
44200             this.split.el.setTop(box.y+box.height);
44201             this.split.el.setWidth(box.width);
44202         }
44203         if(this.collapsed){
44204             this.updateBody(box.width, null);
44205         }
44206         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44207     }
44208 });
44209
44210
44211
44212
44213
44214 Roo.bootstrap.layout.South = function(config){
44215     config.region = 'south';
44216     config.cursor = 's-resize';
44217     Roo.bootstrap.layout.Split.call(this, config);
44218     if(this.split){
44219         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
44220         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44221         this.split.el.addClass("roo-layout-split-v");
44222     }
44223     
44224 };
44225
44226 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
44227     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44228     
44229     onRender : function(ctr, pos)
44230     {
44231         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44232         var size = this.config.initialSize || this.config.height;
44233         if(this.el && typeof size != "undefined"){
44234             this.el.setHeight(size);
44235         }
44236     
44237     },
44238     
44239     getBox : function(){
44240         if(this.collapsed){
44241             return this.collapsedEl.getBox();
44242         }
44243         var box = this.el.getBox();
44244         if(this.split){
44245             var sh = this.split.el.getHeight();
44246             box.height += sh;
44247             box.y -= sh;
44248         }
44249         return box;
44250     },
44251     
44252     updateBox : function(box){
44253         if(this.split && !this.collapsed){
44254             var sh = this.split.el.getHeight();
44255             box.height -= sh;
44256             box.y += sh;
44257             this.split.el.setLeft(box.x);
44258             this.split.el.setTop(box.y-sh);
44259             this.split.el.setWidth(box.width);
44260         }
44261         if(this.collapsed){
44262             this.updateBody(box.width, null);
44263         }
44264         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44265     }
44266 });
44267
44268 Roo.bootstrap.layout.East = function(config){
44269     config.region = "east";
44270     config.cursor = "e-resize";
44271     Roo.bootstrap.layout.Split.call(this, config);
44272     if(this.split){
44273         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
44274         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44275         this.split.el.addClass("roo-layout-split-h");
44276     }
44277     
44278 };
44279 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
44280     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44281     
44282     onRender : function(ctr, pos)
44283     {
44284         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44285         var size = this.config.initialSize || this.config.width;
44286         if(this.el && typeof size != "undefined"){
44287             this.el.setWidth(size);
44288         }
44289     
44290     },
44291     
44292     getBox : function(){
44293         if(this.collapsed){
44294             return this.collapsedEl.getBox();
44295         }
44296         var box = this.el.getBox();
44297         if(this.split){
44298             var sw = this.split.el.getWidth();
44299             box.width += sw;
44300             box.x -= sw;
44301         }
44302         return box;
44303     },
44304
44305     updateBox : function(box){
44306         if(this.split && !this.collapsed){
44307             var sw = this.split.el.getWidth();
44308             box.width -= sw;
44309             this.split.el.setLeft(box.x);
44310             this.split.el.setTop(box.y);
44311             this.split.el.setHeight(box.height);
44312             box.x += sw;
44313         }
44314         if(this.collapsed){
44315             this.updateBody(null, box.height);
44316         }
44317         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44318     }
44319 });
44320
44321 Roo.bootstrap.layout.West = function(config){
44322     config.region = "west";
44323     config.cursor = "w-resize";
44324     
44325     Roo.bootstrap.layout.Split.call(this, config);
44326     if(this.split){
44327         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
44328         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44329         this.split.el.addClass("roo-layout-split-h");
44330     }
44331     
44332 };
44333 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
44334     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44335     
44336     onRender: function(ctr, pos)
44337     {
44338         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
44339         var size = this.config.initialSize || this.config.width;
44340         if(typeof size != "undefined"){
44341             this.el.setWidth(size);
44342         }
44343     },
44344     
44345     getBox : function(){
44346         if(this.collapsed){
44347             return this.collapsedEl.getBox();
44348         }
44349         var box = this.el.getBox();
44350         if (box.width == 0) {
44351             box.width = this.config.width; // kludge?
44352         }
44353         if(this.split){
44354             box.width += this.split.el.getWidth();
44355         }
44356         return box;
44357     },
44358     
44359     updateBox : function(box){
44360         if(this.split && !this.collapsed){
44361             var sw = this.split.el.getWidth();
44362             box.width -= sw;
44363             this.split.el.setLeft(box.x+box.width);
44364             this.split.el.setTop(box.y);
44365             this.split.el.setHeight(box.height);
44366         }
44367         if(this.collapsed){
44368             this.updateBody(null, box.height);
44369         }
44370         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44371     }
44372 });/*
44373  * Based on:
44374  * Ext JS Library 1.1.1
44375  * Copyright(c) 2006-2007, Ext JS, LLC.
44376  *
44377  * Originally Released Under LGPL - original licence link has changed is not relivant.
44378  *
44379  * Fork - LGPL
44380  * <script type="text/javascript">
44381  */
44382 /**
44383  * @class Roo.bootstrap.paenl.Content
44384  * @extends Roo.util.Observable
44385  * @children Roo.bootstrap.Component
44386  * @parent builder Roo.bootstrap.layout.Border
44387  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
44388  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
44389  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
44390  * @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
44391  * @cfg {Boolean}   closable      True if the panel can be closed/removed
44392  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
44393  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44394  * @cfg {Toolbar}   toolbar       A toolbar for this panel
44395  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
44396  * @cfg {String} title          The title for this panel
44397  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44398  * @cfg {String} url            Calls {@link #setUrl} with this value
44399  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
44400  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
44401  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
44402  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
44403  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
44404  * @cfg {Boolean} badges render the badges
44405  * @cfg {String} cls  extra classes to use  
44406  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
44407  
44408  * @constructor
44409  * Create a new ContentPanel.
44410  * @param {String/Object} config A string to set only the title or a config object
44411  
44412  */
44413 Roo.bootstrap.panel.Content = function( config){
44414     
44415     this.tpl = config.tpl || false;
44416     
44417     var el = config.el;
44418     var content = config.content;
44419
44420     if(config.autoCreate){ // xtype is available if this is called from factory
44421         el = Roo.id();
44422     }
44423     this.el = Roo.get(el);
44424     if(!this.el && config && config.autoCreate){
44425         if(typeof config.autoCreate == "object"){
44426             if(!config.autoCreate.id){
44427                 config.autoCreate.id = config.id||el;
44428             }
44429             this.el = Roo.DomHelper.append(document.body,
44430                         config.autoCreate, true);
44431         }else{
44432             var elcfg =  {
44433                 tag: "div",
44434                 cls: (config.cls || '') +
44435                     (config.background ? ' bg-' + config.background : '') +
44436                     " roo-layout-inactive-content",
44437                 id: config.id||el
44438             };
44439             if (config.iframe) {
44440                 elcfg.cn = [
44441                     {
44442                         tag : 'iframe',
44443                         style : 'border: 0px',
44444                         src : 'about:blank'
44445                     }
44446                 ];
44447             }
44448               
44449             if (config.html) {
44450                 elcfg.html = config.html;
44451                 
44452             }
44453                         
44454             this.el = Roo.DomHelper.append(document.body, elcfg , true);
44455             if (config.iframe) {
44456                 this.iframeEl = this.el.select('iframe',true).first();
44457             }
44458             
44459         }
44460     } 
44461     this.closable = false;
44462     this.loaded = false;
44463     this.active = false;
44464    
44465       
44466     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
44467         
44468         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
44469         
44470         this.wrapEl = this.el; //this.el.wrap();
44471         var ti = [];
44472         if (config.toolbar.items) {
44473             ti = config.toolbar.items ;
44474             delete config.toolbar.items ;
44475         }
44476         
44477         var nitems = [];
44478         this.toolbar.render(this.wrapEl, 'before');
44479         for(var i =0;i < ti.length;i++) {
44480           //  Roo.log(['add child', items[i]]);
44481             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
44482         }
44483         this.toolbar.items = nitems;
44484         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
44485         delete config.toolbar;
44486         
44487     }
44488     /*
44489     // xtype created footer. - not sure if will work as we normally have to render first..
44490     if (this.footer && !this.footer.el && this.footer.xtype) {
44491         if (!this.wrapEl) {
44492             this.wrapEl = this.el.wrap();
44493         }
44494     
44495         this.footer.container = this.wrapEl.createChild();
44496          
44497         this.footer = Roo.factory(this.footer, Roo);
44498         
44499     }
44500     */
44501     
44502      if(typeof config == "string"){
44503         this.title = config;
44504     }else{
44505         Roo.apply(this, config);
44506     }
44507     
44508     if(this.resizeEl){
44509         this.resizeEl = Roo.get(this.resizeEl, true);
44510     }else{
44511         this.resizeEl = this.el;
44512     }
44513     // handle view.xtype
44514     
44515  
44516     
44517     
44518     this.addEvents({
44519         /**
44520          * @event activate
44521          * Fires when this panel is activated. 
44522          * @param {Roo.ContentPanel} this
44523          */
44524         "activate" : true,
44525         /**
44526          * @event deactivate
44527          * Fires when this panel is activated. 
44528          * @param {Roo.ContentPanel} this
44529          */
44530         "deactivate" : true,
44531
44532         /**
44533          * @event resize
44534          * Fires when this panel is resized if fitToFrame is true.
44535          * @param {Roo.ContentPanel} this
44536          * @param {Number} width The width after any component adjustments
44537          * @param {Number} height The height after any component adjustments
44538          */
44539         "resize" : true,
44540         
44541          /**
44542          * @event render
44543          * Fires when this tab is created
44544          * @param {Roo.ContentPanel} this
44545          */
44546         "render" : true,
44547         
44548           /**
44549          * @event scroll
44550          * Fires when this content is scrolled
44551          * @param {Roo.ContentPanel} this
44552          * @param {Event} scrollEvent
44553          */
44554         "scroll" : true
44555         
44556         
44557         
44558     });
44559     
44560
44561     
44562     
44563     if(this.autoScroll && !this.iframe){
44564         this.resizeEl.setStyle("overflow", "auto");
44565         this.resizeEl.on('scroll', this.onScroll, this);
44566     } else {
44567         // fix randome scrolling
44568         //this.el.on('scroll', function() {
44569         //    Roo.log('fix random scolling');
44570         //    this.scrollTo('top',0); 
44571         //});
44572     }
44573     content = content || this.content;
44574     if(content){
44575         this.setContent(content);
44576     }
44577     if(config && config.url){
44578         this.setUrl(this.url, this.params, this.loadOnce);
44579     }
44580     
44581     
44582     
44583     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
44584     
44585     if (this.view && typeof(this.view.xtype) != 'undefined') {
44586         this.view.el = this.el.appendChild(document.createElement("div"));
44587         this.view = Roo.factory(this.view); 
44588         this.view.render  &&  this.view.render(false, '');  
44589     }
44590     
44591     
44592     this.fireEvent('render', this);
44593 };
44594
44595 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
44596     
44597     cls : '',
44598     background : '',
44599     
44600     tabTip : '',
44601     
44602     iframe : false,
44603     iframeEl : false,
44604     
44605     /* Resize Element - use this to work out scroll etc. */
44606     resizeEl : false,
44607     
44608     setRegion : function(region){
44609         this.region = region;
44610         this.setActiveClass(region && !this.background);
44611     },
44612     
44613     
44614     setActiveClass: function(state)
44615     {
44616         if(state){
44617            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
44618            this.el.setStyle('position','relative');
44619         }else{
44620            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
44621            this.el.setStyle('position', 'absolute');
44622         } 
44623     },
44624     
44625     /**
44626      * Returns the toolbar for this Panel if one was configured. 
44627      * @return {Roo.Toolbar} 
44628      */
44629     getToolbar : function(){
44630         return this.toolbar;
44631     },
44632     
44633     setActiveState : function(active)
44634     {
44635         this.active = active;
44636         this.setActiveClass(active);
44637         if(!active){
44638             if(this.fireEvent("deactivate", this) === false){
44639                 return false;
44640             }
44641             return true;
44642         }
44643         this.fireEvent("activate", this);
44644         return true;
44645     },
44646     /**
44647      * Updates this panel's element (not for iframe)
44648      * @param {String} content The new content
44649      * @param {Boolean} loadScripts (optional) true to look for and process scripts
44650     */
44651     setContent : function(content, loadScripts){
44652         if (this.iframe) {
44653             return;
44654         }
44655         
44656         this.el.update(content, loadScripts);
44657     },
44658
44659     ignoreResize : function(w, h)
44660     {
44661         //return false; // always resize?
44662         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
44663             return true;
44664         }else{
44665             this.lastSize = {width: w, height: h};
44666             return false;
44667         }
44668     },
44669     /**
44670      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
44671      * @return {Roo.UpdateManager} The UpdateManager
44672      */
44673     getUpdateManager : function(){
44674         if (this.iframe) {
44675             return false;
44676         }
44677         return this.el.getUpdateManager();
44678     },
44679      /**
44680      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
44681      * Does not work with IFRAME contents
44682      * @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:
44683 <pre><code>
44684 panel.load({
44685     url: "your-url.php",
44686     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
44687     callback: yourFunction,
44688     scope: yourObject, //(optional scope)
44689     discardUrl: false,
44690     nocache: false,
44691     text: "Loading...",
44692     timeout: 30,
44693     scripts: false
44694 });
44695 </code></pre>
44696      
44697      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
44698      * 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.
44699      * @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}
44700      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
44701      * @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.
44702      * @return {Roo.ContentPanel} this
44703      */
44704     load : function(){
44705         
44706         if (this.iframe) {
44707             return this;
44708         }
44709         
44710         var um = this.el.getUpdateManager();
44711         um.update.apply(um, arguments);
44712         return this;
44713     },
44714
44715
44716     /**
44717      * 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.
44718      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
44719      * @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)
44720      * @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)
44721      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
44722      */
44723     setUrl : function(url, params, loadOnce){
44724         if (this.iframe) {
44725             this.iframeEl.dom.src = url;
44726             return false;
44727         }
44728         
44729         if(this.refreshDelegate){
44730             this.removeListener("activate", this.refreshDelegate);
44731         }
44732         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44733         this.on("activate", this.refreshDelegate);
44734         return this.el.getUpdateManager();
44735     },
44736     
44737     _handleRefresh : function(url, params, loadOnce){
44738         if(!loadOnce || !this.loaded){
44739             var updater = this.el.getUpdateManager();
44740             updater.update(url, params, this._setLoaded.createDelegate(this));
44741         }
44742     },
44743     
44744     _setLoaded : function(){
44745         this.loaded = true;
44746     }, 
44747     
44748     /**
44749      * Returns this panel's id
44750      * @return {String} 
44751      */
44752     getId : function(){
44753         return this.el.id;
44754     },
44755     
44756     /** 
44757      * Returns this panel's element - used by regiosn to add.
44758      * @return {Roo.Element} 
44759      */
44760     getEl : function(){
44761         return this.wrapEl || this.el;
44762     },
44763     
44764    
44765     
44766     adjustForComponents : function(width, height)
44767     {
44768         //Roo.log('adjustForComponents ');
44769         if(this.resizeEl != this.el){
44770             width -= this.el.getFrameWidth('lr');
44771             height -= this.el.getFrameWidth('tb');
44772         }
44773         if(this.toolbar){
44774             var te = this.toolbar.getEl();
44775             te.setWidth(width);
44776             height -= te.getHeight();
44777         }
44778         if(this.footer){
44779             var te = this.footer.getEl();
44780             te.setWidth(width);
44781             height -= te.getHeight();
44782         }
44783         
44784         
44785         if(this.adjustments){
44786             width += this.adjustments[0];
44787             height += this.adjustments[1];
44788         }
44789         return {"width": width, "height": height};
44790     },
44791     
44792     setSize : function(width, height){
44793         if(this.fitToFrame && !this.ignoreResize(width, height)){
44794             if(this.fitContainer && this.resizeEl != this.el){
44795                 this.el.setSize(width, height);
44796             }
44797             var size = this.adjustForComponents(width, height);
44798             if (this.iframe) {
44799                 this.iframeEl.setSize(width,height);
44800             }
44801             
44802             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
44803             this.fireEvent('resize', this, size.width, size.height);
44804             
44805             
44806         }
44807     },
44808     
44809     /**
44810      * Returns this panel's title
44811      * @return {String} 
44812      */
44813     getTitle : function(){
44814         
44815         if (typeof(this.title) != 'object') {
44816             return this.title;
44817         }
44818         
44819         var t = '';
44820         for (var k in this.title) {
44821             if (!this.title.hasOwnProperty(k)) {
44822                 continue;
44823             }
44824             
44825             if (k.indexOf('-') >= 0) {
44826                 var s = k.split('-');
44827                 for (var i = 0; i<s.length; i++) {
44828                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
44829                 }
44830             } else {
44831                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
44832             }
44833         }
44834         return t;
44835     },
44836     
44837     /**
44838      * Set this panel's title
44839      * @param {String} title
44840      */
44841     setTitle : function(title){
44842         this.title = title;
44843         if(this.region){
44844             this.region.updatePanelTitle(this, title);
44845         }
44846     },
44847     
44848     /**
44849      * Returns true is this panel was configured to be closable
44850      * @return {Boolean} 
44851      */
44852     isClosable : function(){
44853         return this.closable;
44854     },
44855     
44856     beforeSlide : function(){
44857         this.el.clip();
44858         this.resizeEl.clip();
44859     },
44860     
44861     afterSlide : function(){
44862         this.el.unclip();
44863         this.resizeEl.unclip();
44864     },
44865     
44866     /**
44867      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
44868      *   Will fail silently if the {@link #setUrl} method has not been called.
44869      *   This does not activate the panel, just updates its content.
44870      */
44871     refresh : function(){
44872         if(this.refreshDelegate){
44873            this.loaded = false;
44874            this.refreshDelegate();
44875         }
44876     },
44877     
44878     /**
44879      * Destroys this panel
44880      */
44881     destroy : function(){
44882         this.el.removeAllListeners();
44883         var tempEl = document.createElement("span");
44884         tempEl.appendChild(this.el.dom);
44885         tempEl.innerHTML = "";
44886         this.el.remove();
44887         this.el = null;
44888     },
44889     
44890     /**
44891      * form - if the content panel contains a form - this is a reference to it.
44892      * @type {Roo.form.Form}
44893      */
44894     form : false,
44895     /**
44896      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
44897      *    This contains a reference to it.
44898      * @type {Roo.View}
44899      */
44900     view : false,
44901     
44902       /**
44903      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
44904      * <pre><code>
44905
44906 layout.addxtype({
44907        xtype : 'Form',
44908        items: [ .... ]
44909    }
44910 );
44911
44912 </code></pre>
44913      * @param {Object} cfg Xtype definition of item to add.
44914      */
44915     
44916     
44917     getChildContainer: function () {
44918         return this.getEl();
44919     },
44920     
44921     
44922     onScroll : function(e)
44923     {
44924         this.fireEvent('scroll', this, e);
44925     }
44926     
44927     
44928     /*
44929         var  ret = new Roo.factory(cfg);
44930         return ret;
44931         
44932         
44933         // add form..
44934         if (cfg.xtype.match(/^Form$/)) {
44935             
44936             var el;
44937             //if (this.footer) {
44938             //    el = this.footer.container.insertSibling(false, 'before');
44939             //} else {
44940                 el = this.el.createChild();
44941             //}
44942
44943             this.form = new  Roo.form.Form(cfg);
44944             
44945             
44946             if ( this.form.allItems.length) {
44947                 this.form.render(el.dom);
44948             }
44949             return this.form;
44950         }
44951         // should only have one of theses..
44952         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
44953             // views.. should not be just added - used named prop 'view''
44954             
44955             cfg.el = this.el.appendChild(document.createElement("div"));
44956             // factory?
44957             
44958             var ret = new Roo.factory(cfg);
44959              
44960              ret.render && ret.render(false, ''); // render blank..
44961             this.view = ret;
44962             return ret;
44963         }
44964         return false;
44965     }
44966     \*/
44967 });
44968  
44969 /**
44970  * @class Roo.bootstrap.panel.Grid
44971  * @extends Roo.bootstrap.panel.Content
44972  * @constructor
44973  * Create a new GridPanel.
44974  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
44975  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
44976  * @param {Object} config A the config object
44977   
44978  */
44979
44980
44981
44982 Roo.bootstrap.panel.Grid = function(config)
44983 {
44984     
44985       
44986     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
44987         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
44988
44989     config.el = this.wrapper;
44990     //this.el = this.wrapper;
44991     
44992       if (config.container) {
44993         // ctor'ed from a Border/panel.grid
44994         
44995         
44996         this.wrapper.setStyle("overflow", "hidden");
44997         this.wrapper.addClass('roo-grid-container');
44998
44999     }
45000     
45001     
45002     if(config.toolbar){
45003         var tool_el = this.wrapper.createChild();    
45004         this.toolbar = Roo.factory(config.toolbar);
45005         var ti = [];
45006         if (config.toolbar.items) {
45007             ti = config.toolbar.items ;
45008             delete config.toolbar.items ;
45009         }
45010         
45011         var nitems = [];
45012         this.toolbar.render(tool_el);
45013         for(var i =0;i < ti.length;i++) {
45014           //  Roo.log(['add child', items[i]]);
45015             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45016         }
45017         this.toolbar.items = nitems;
45018         
45019         delete config.toolbar;
45020     }
45021     
45022     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45023     config.grid.scrollBody = true;;
45024     config.grid.monitorWindowResize = false; // turn off autosizing
45025     config.grid.autoHeight = false;
45026     config.grid.autoWidth = false;
45027     
45028     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45029     
45030     if (config.background) {
45031         // render grid on panel activation (if panel background)
45032         this.on('activate', function(gp) {
45033             if (!gp.grid.rendered) {
45034                 gp.grid.render(this.wrapper);
45035                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
45036             }
45037         });
45038             
45039     } else {
45040         this.grid.render(this.wrapper);
45041         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
45042
45043     }
45044     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45045     // ??? needed ??? config.el = this.wrapper;
45046     
45047     
45048     
45049   
45050     // xtype created footer. - not sure if will work as we normally have to render first..
45051     if (this.footer && !this.footer.el && this.footer.xtype) {
45052         
45053         var ctr = this.grid.getView().getFooterPanel(true);
45054         this.footer.dataSource = this.grid.dataSource;
45055         this.footer = Roo.factory(this.footer, Roo);
45056         this.footer.render(ctr);
45057         
45058     }
45059     
45060     
45061     
45062     
45063      
45064 };
45065
45066 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45067 {
45068   
45069     getId : function(){
45070         return this.grid.id;
45071     },
45072     
45073     /**
45074      * Returns the grid for this panel
45075      * @return {Roo.bootstrap.Table} 
45076      */
45077     getGrid : function(){
45078         return this.grid;    
45079     },
45080     
45081     setSize : function(width, height)
45082     {
45083      
45084         //if(!this.ignoreResize(width, height)){
45085             var grid = this.grid;
45086             var size = this.adjustForComponents(width, height);
45087             // tfoot is not a footer?
45088           
45089             
45090             var gridel = grid.getGridEl();
45091             gridel.setSize(size.width, size.height);
45092             
45093             var tbd = grid.getGridEl().select('tbody', true).first();
45094             var thd = grid.getGridEl().select('thead',true).first();
45095             var tbf= grid.getGridEl().select('tfoot', true).first();
45096
45097             if (tbf) {
45098                 size.height -= tbf.getHeight();
45099             }
45100             if (thd) {
45101                 size.height -= thd.getHeight();
45102             }
45103             
45104             tbd.setSize(size.width, size.height );
45105             // this is for the account management tab -seems to work there.
45106             var thd = grid.getGridEl().select('thead',true).first();
45107             //if (tbd) {
45108             //    tbd.setSize(size.width, size.height - thd.getHeight());
45109             //}
45110              
45111             grid.autoSize();
45112         //}
45113    
45114     },
45115      
45116     
45117     
45118     beforeSlide : function(){
45119         this.grid.getView().scroller.clip();
45120     },
45121     
45122     afterSlide : function(){
45123         this.grid.getView().scroller.unclip();
45124     },
45125     
45126     destroy : function(){
45127         this.grid.destroy();
45128         delete this.grid;
45129         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
45130     }
45131 });
45132
45133 /**
45134  * @class Roo.bootstrap.panel.Nest
45135  * @extends Roo.bootstrap.panel.Content
45136  * @constructor
45137  * Create a new Panel, that can contain a layout.Border.
45138  * 
45139  * 
45140  * @param {String/Object} config A string to set only the title or a config object
45141  */
45142 Roo.bootstrap.panel.Nest = function(config)
45143 {
45144     // construct with only one argument..
45145     /* FIXME - implement nicer consturctors
45146     if (layout.layout) {
45147         config = layout;
45148         layout = config.layout;
45149         delete config.layout;
45150     }
45151     if (layout.xtype && !layout.getEl) {
45152         // then layout needs constructing..
45153         layout = Roo.factory(layout, Roo);
45154     }
45155     */
45156     
45157     config.el =  config.layout.getEl();
45158     
45159     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
45160     
45161     config.layout.monitorWindowResize = false; // turn off autosizing
45162     this.layout = config.layout;
45163     this.layout.getEl().addClass("roo-layout-nested-layout");
45164     this.layout.parent = this;
45165     
45166     
45167     
45168     
45169 };
45170
45171 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
45172     /**
45173     * @cfg {Roo.BorderLayout} layout The layout for this panel
45174     */
45175     layout : false,
45176
45177     setSize : function(width, height){
45178         if(!this.ignoreResize(width, height)){
45179             var size = this.adjustForComponents(width, height);
45180             var el = this.layout.getEl();
45181             if (size.height < 1) {
45182                 el.setWidth(size.width);   
45183             } else {
45184                 el.setSize(size.width, size.height);
45185             }
45186             var touch = el.dom.offsetWidth;
45187             this.layout.layout();
45188             // ie requires a double layout on the first pass
45189             if(Roo.isIE && !this.initialized){
45190                 this.initialized = true;
45191                 this.layout.layout();
45192             }
45193         }
45194     },
45195     
45196     // activate all subpanels if not currently active..
45197     
45198     setActiveState : function(active){
45199         this.active = active;
45200         this.setActiveClass(active);
45201         
45202         if(!active){
45203             this.fireEvent("deactivate", this);
45204             return;
45205         }
45206         
45207         this.fireEvent("activate", this);
45208         // not sure if this should happen before or after..
45209         if (!this.layout) {
45210             return; // should not happen..
45211         }
45212         var reg = false;
45213         for (var r in this.layout.regions) {
45214             reg = this.layout.getRegion(r);
45215             if (reg.getActivePanel()) {
45216                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
45217                 reg.setActivePanel(reg.getActivePanel());
45218                 continue;
45219             }
45220             if (!reg.panels.length) {
45221                 continue;
45222             }
45223             reg.showPanel(reg.getPanel(0));
45224         }
45225         
45226         
45227         
45228         
45229     },
45230     
45231     /**
45232      * Returns the nested BorderLayout for this panel
45233      * @return {Roo.BorderLayout} 
45234      */
45235     getLayout : function(){
45236         return this.layout;
45237     },
45238     
45239      /**
45240      * Adds a xtype elements to the layout of the nested panel
45241      * <pre><code>
45242
45243 panel.addxtype({
45244        xtype : 'ContentPanel',
45245        region: 'west',
45246        items: [ .... ]
45247    }
45248 );
45249
45250 panel.addxtype({
45251         xtype : 'NestedLayoutPanel',
45252         region: 'west',
45253         layout: {
45254            center: { },
45255            west: { }   
45256         },
45257         items : [ ... list of content panels or nested layout panels.. ]
45258    }
45259 );
45260 </code></pre>
45261      * @param {Object} cfg Xtype definition of item to add.
45262      */
45263     addxtype : function(cfg) {
45264         return this.layout.addxtype(cfg);
45265     
45266     }
45267 });/*
45268  * Based on:
45269  * Ext JS Library 1.1.1
45270  * Copyright(c) 2006-2007, Ext JS, LLC.
45271  *
45272  * Originally Released Under LGPL - original licence link has changed is not relivant.
45273  *
45274  * Fork - LGPL
45275  * <script type="text/javascript">
45276  */
45277 /**
45278  * @class Roo.TabPanel
45279  * @extends Roo.util.Observable
45280  * A lightweight tab container.
45281  * <br><br>
45282  * Usage:
45283  * <pre><code>
45284 // basic tabs 1, built from existing content
45285 var tabs = new Roo.TabPanel("tabs1");
45286 tabs.addTab("script", "View Script");
45287 tabs.addTab("markup", "View Markup");
45288 tabs.activate("script");
45289
45290 // more advanced tabs, built from javascript
45291 var jtabs = new Roo.TabPanel("jtabs");
45292 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
45293
45294 // set up the UpdateManager
45295 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
45296 var updater = tab2.getUpdateManager();
45297 updater.setDefaultUrl("ajax1.htm");
45298 tab2.on('activate', updater.refresh, updater, true);
45299
45300 // Use setUrl for Ajax loading
45301 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
45302 tab3.setUrl("ajax2.htm", null, true);
45303
45304 // Disabled tab
45305 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
45306 tab4.disable();
45307
45308 jtabs.activate("jtabs-1");
45309  * </code></pre>
45310  * @constructor
45311  * Create a new TabPanel.
45312  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
45313  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
45314  */
45315 Roo.bootstrap.panel.Tabs = function(config){
45316     /**
45317     * The container element for this TabPanel.
45318     * @type Roo.Element
45319     */
45320     this.el = Roo.get(config.el);
45321     delete config.el;
45322     if(config){
45323         if(typeof config == "boolean"){
45324             this.tabPosition = config ? "bottom" : "top";
45325         }else{
45326             Roo.apply(this, config);
45327         }
45328     }
45329     
45330     if(this.tabPosition == "bottom"){
45331         // if tabs are at the bottom = create the body first.
45332         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45333         this.el.addClass("roo-tabs-bottom");
45334     }
45335     // next create the tabs holders
45336     
45337     if (this.tabPosition == "west"){
45338         
45339         var reg = this.region; // fake it..
45340         while (reg) {
45341             if (!reg.mgr.parent) {
45342                 break;
45343             }
45344             reg = reg.mgr.parent.region;
45345         }
45346         Roo.log("got nest?");
45347         Roo.log(reg);
45348         if (reg.mgr.getRegion('west')) {
45349             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
45350             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
45351             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45352             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45353             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45354         
45355             
45356         }
45357         
45358         
45359     } else {
45360      
45361         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
45362         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45363         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45364         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45365     }
45366     
45367     
45368     if(Roo.isIE){
45369         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
45370     }
45371     
45372     // finally - if tabs are at the top, then create the body last..
45373     if(this.tabPosition != "bottom"){
45374         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
45375          * @type Roo.Element
45376          */
45377         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45378         this.el.addClass("roo-tabs-top");
45379     }
45380     this.items = [];
45381
45382     this.bodyEl.setStyle("position", "relative");
45383
45384     this.active = null;
45385     this.activateDelegate = this.activate.createDelegate(this);
45386
45387     this.addEvents({
45388         /**
45389          * @event tabchange
45390          * Fires when the active tab changes
45391          * @param {Roo.TabPanel} this
45392          * @param {Roo.TabPanelItem} activePanel The new active tab
45393          */
45394         "tabchange": true,
45395         /**
45396          * @event beforetabchange
45397          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
45398          * @param {Roo.TabPanel} this
45399          * @param {Object} e Set cancel to true on this object to cancel the tab change
45400          * @param {Roo.TabPanelItem} tab The tab being changed to
45401          */
45402         "beforetabchange" : true
45403     });
45404
45405     Roo.EventManager.onWindowResize(this.onResize, this);
45406     this.cpad = this.el.getPadding("lr");
45407     this.hiddenCount = 0;
45408
45409
45410     // toolbar on the tabbar support...
45411     if (this.toolbar) {
45412         alert("no toolbar support yet");
45413         this.toolbar  = false;
45414         /*
45415         var tcfg = this.toolbar;
45416         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
45417         this.toolbar = new Roo.Toolbar(tcfg);
45418         if (Roo.isSafari) {
45419             var tbl = tcfg.container.child('table', true);
45420             tbl.setAttribute('width', '100%');
45421         }
45422         */
45423         
45424     }
45425    
45426
45427
45428     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
45429 };
45430
45431 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
45432     /*
45433      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
45434      */
45435     tabPosition : "top",
45436     /*
45437      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
45438      */
45439     currentTabWidth : 0,
45440     /*
45441      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
45442      */
45443     minTabWidth : 40,
45444     /*
45445      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
45446      */
45447     maxTabWidth : 250,
45448     /*
45449      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
45450      */
45451     preferredTabWidth : 175,
45452     /*
45453      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
45454      */
45455     resizeTabs : false,
45456     /*
45457      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
45458      */
45459     monitorResize : true,
45460     /*
45461      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
45462      */
45463     toolbar : false,  // set by caller..
45464     
45465     region : false, /// set by caller
45466     
45467     disableTooltips : true, // not used yet...
45468
45469     /**
45470      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
45471      * @param {String} id The id of the div to use <b>or create</b>
45472      * @param {String} text The text for the tab
45473      * @param {String} content (optional) Content to put in the TabPanelItem body
45474      * @param {Boolean} closable (optional) True to create a close icon on the tab
45475      * @return {Roo.TabPanelItem} The created TabPanelItem
45476      */
45477     addTab : function(id, text, content, closable, tpl)
45478     {
45479         var item = new Roo.bootstrap.panel.TabItem({
45480             panel: this,
45481             id : id,
45482             text : text,
45483             closable : closable,
45484             tpl : tpl
45485         });
45486         this.addTabItem(item);
45487         if(content){
45488             item.setContent(content);
45489         }
45490         return item;
45491     },
45492
45493     /**
45494      * Returns the {@link Roo.TabPanelItem} with the specified id/index
45495      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
45496      * @return {Roo.TabPanelItem}
45497      */
45498     getTab : function(id){
45499         return this.items[id];
45500     },
45501
45502     /**
45503      * Hides the {@link Roo.TabPanelItem} with the specified id/index
45504      * @param {String/Number} id The id or index of the TabPanelItem to hide.
45505      */
45506     hideTab : function(id){
45507         var t = this.items[id];
45508         if(!t.isHidden()){
45509            t.setHidden(true);
45510            this.hiddenCount++;
45511            this.autoSizeTabs();
45512         }
45513     },
45514
45515     /**
45516      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
45517      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
45518      */
45519     unhideTab : function(id){
45520         var t = this.items[id];
45521         if(t.isHidden()){
45522            t.setHidden(false);
45523            this.hiddenCount--;
45524            this.autoSizeTabs();
45525         }
45526     },
45527
45528     /**
45529      * Adds an existing {@link Roo.TabPanelItem}.
45530      * @param {Roo.TabPanelItem} item The TabPanelItem to add
45531      */
45532     addTabItem : function(item)
45533     {
45534         this.items[item.id] = item;
45535         this.items.push(item);
45536         this.autoSizeTabs();
45537       //  if(this.resizeTabs){
45538     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
45539   //         this.autoSizeTabs();
45540 //        }else{
45541 //            item.autoSize();
45542        // }
45543     },
45544
45545     /**
45546      * Removes a {@link Roo.TabPanelItem}.
45547      * @param {String/Number} id The id or index of the TabPanelItem to remove.
45548      */
45549     removeTab : function(id){
45550         var items = this.items;
45551         var tab = items[id];
45552         if(!tab) { return; }
45553         var index = items.indexOf(tab);
45554         if(this.active == tab && items.length > 1){
45555             var newTab = this.getNextAvailable(index);
45556             if(newTab) {
45557                 newTab.activate();
45558             }
45559         }
45560         this.stripEl.dom.removeChild(tab.pnode.dom);
45561         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
45562             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
45563         }
45564         items.splice(index, 1);
45565         delete this.items[tab.id];
45566         tab.fireEvent("close", tab);
45567         tab.purgeListeners();
45568         this.autoSizeTabs();
45569     },
45570
45571     getNextAvailable : function(start){
45572         var items = this.items;
45573         var index = start;
45574         // look for a next tab that will slide over to
45575         // replace the one being removed
45576         while(index < items.length){
45577             var item = items[++index];
45578             if(item && !item.isHidden()){
45579                 return item;
45580             }
45581         }
45582         // if one isn't found select the previous tab (on the left)
45583         index = start;
45584         while(index >= 0){
45585             var item = items[--index];
45586             if(item && !item.isHidden()){
45587                 return item;
45588             }
45589         }
45590         return null;
45591     },
45592
45593     /**
45594      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
45595      * @param {String/Number} id The id or index of the TabPanelItem to disable.
45596      */
45597     disableTab : function(id){
45598         var tab = this.items[id];
45599         if(tab && this.active != tab){
45600             tab.disable();
45601         }
45602     },
45603
45604     /**
45605      * Enables a {@link Roo.TabPanelItem} that is disabled.
45606      * @param {String/Number} id The id or index of the TabPanelItem to enable.
45607      */
45608     enableTab : function(id){
45609         var tab = this.items[id];
45610         tab.enable();
45611     },
45612
45613     /**
45614      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
45615      * @param {String/Number} id The id or index of the TabPanelItem to activate.
45616      * @return {Roo.TabPanelItem} The TabPanelItem.
45617      */
45618     activate : function(id)
45619     {
45620         //Roo.log('activite:'  + id);
45621         
45622         var tab = this.items[id];
45623         if(!tab){
45624             return null;
45625         }
45626         if(tab == this.active || tab.disabled){
45627             return tab;
45628         }
45629         var e = {};
45630         this.fireEvent("beforetabchange", this, e, tab);
45631         if(e.cancel !== true && !tab.disabled){
45632             if(this.active){
45633                 this.active.hide();
45634             }
45635             this.active = this.items[id];
45636             this.active.show();
45637             this.fireEvent("tabchange", this, this.active);
45638         }
45639         return tab;
45640     },
45641
45642     /**
45643      * Gets the active {@link Roo.TabPanelItem}.
45644      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
45645      */
45646     getActiveTab : function(){
45647         return this.active;
45648     },
45649
45650     /**
45651      * Updates the tab body element to fit the height of the container element
45652      * for overflow scrolling
45653      * @param {Number} targetHeight (optional) Override the starting height from the elements height
45654      */
45655     syncHeight : function(targetHeight){
45656         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
45657         var bm = this.bodyEl.getMargins();
45658         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
45659         this.bodyEl.setHeight(newHeight);
45660         return newHeight;
45661     },
45662
45663     onResize : function(){
45664         if(this.monitorResize){
45665             this.autoSizeTabs();
45666         }
45667     },
45668
45669     /**
45670      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
45671      */
45672     beginUpdate : function(){
45673         this.updating = true;
45674     },
45675
45676     /**
45677      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
45678      */
45679     endUpdate : function(){
45680         this.updating = false;
45681         this.autoSizeTabs();
45682     },
45683
45684     /**
45685      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
45686      */
45687     autoSizeTabs : function()
45688     {
45689         var count = this.items.length;
45690         var vcount = count - this.hiddenCount;
45691         
45692         if (vcount < 2) {
45693             this.stripEl.hide();
45694         } else {
45695             this.stripEl.show();
45696         }
45697         
45698         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
45699             return;
45700         }
45701         
45702         
45703         var w = Math.max(this.el.getWidth() - this.cpad, 10);
45704         var availWidth = Math.floor(w / vcount);
45705         var b = this.stripBody;
45706         if(b.getWidth() > w){
45707             var tabs = this.items;
45708             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
45709             if(availWidth < this.minTabWidth){
45710                 /*if(!this.sleft){    // incomplete scrolling code
45711                     this.createScrollButtons();
45712                 }
45713                 this.showScroll();
45714                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
45715             }
45716         }else{
45717             if(this.currentTabWidth < this.preferredTabWidth){
45718                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
45719             }
45720         }
45721     },
45722
45723     /**
45724      * Returns the number of tabs in this TabPanel.
45725      * @return {Number}
45726      */
45727      getCount : function(){
45728          return this.items.length;
45729      },
45730
45731     /**
45732      * Resizes all the tabs to the passed width
45733      * @param {Number} The new width
45734      */
45735     setTabWidth : function(width){
45736         this.currentTabWidth = width;
45737         for(var i = 0, len = this.items.length; i < len; i++) {
45738                 if(!this.items[i].isHidden()) {
45739                 this.items[i].setWidth(width);
45740             }
45741         }
45742     },
45743
45744     /**
45745      * Destroys this TabPanel
45746      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
45747      */
45748     destroy : function(removeEl){
45749         Roo.EventManager.removeResizeListener(this.onResize, this);
45750         for(var i = 0, len = this.items.length; i < len; i++){
45751             this.items[i].purgeListeners();
45752         }
45753         if(removeEl === true){
45754             this.el.update("");
45755             this.el.remove();
45756         }
45757     },
45758     
45759     createStrip : function(container)
45760     {
45761         var strip = document.createElement("nav");
45762         strip.className = Roo.bootstrap.version == 4 ?
45763             "navbar-light bg-light" : 
45764             "navbar navbar-default"; //"x-tabs-wrap";
45765         container.appendChild(strip);
45766         return strip;
45767     },
45768     
45769     createStripList : function(strip)
45770     {
45771         // div wrapper for retard IE
45772         // returns the "tr" element.
45773         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
45774         //'<div class="x-tabs-strip-wrap">'+
45775           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
45776           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
45777         return strip.firstChild; //.firstChild.firstChild.firstChild;
45778     },
45779     createBody : function(container)
45780     {
45781         var body = document.createElement("div");
45782         Roo.id(body, "tab-body");
45783         //Roo.fly(body).addClass("x-tabs-body");
45784         Roo.fly(body).addClass("tab-content");
45785         container.appendChild(body);
45786         return body;
45787     },
45788     createItemBody :function(bodyEl, id){
45789         var body = Roo.getDom(id);
45790         if(!body){
45791             body = document.createElement("div");
45792             body.id = id;
45793         }
45794         //Roo.fly(body).addClass("x-tabs-item-body");
45795         Roo.fly(body).addClass("tab-pane");
45796          bodyEl.insertBefore(body, bodyEl.firstChild);
45797         return body;
45798     },
45799     /** @private */
45800     createStripElements :  function(stripEl, text, closable, tpl)
45801     {
45802         var td = document.createElement("li"); // was td..
45803         td.className = 'nav-item';
45804         
45805         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
45806         
45807         
45808         stripEl.appendChild(td);
45809         /*if(closable){
45810             td.className = "x-tabs-closable";
45811             if(!this.closeTpl){
45812                 this.closeTpl = new Roo.Template(
45813                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45814                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
45815                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
45816                 );
45817             }
45818             var el = this.closeTpl.overwrite(td, {"text": text});
45819             var close = el.getElementsByTagName("div")[0];
45820             var inner = el.getElementsByTagName("em")[0];
45821             return {"el": el, "close": close, "inner": inner};
45822         } else {
45823         */
45824         // not sure what this is..
45825 //            if(!this.tabTpl){
45826                 //this.tabTpl = new Roo.Template(
45827                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45828                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
45829                 //);
45830 //                this.tabTpl = new Roo.Template(
45831 //                   '<a href="#">' +
45832 //                   '<span unselectable="on"' +
45833 //                            (this.disableTooltips ? '' : ' title="{text}"') +
45834 //                            ' >{text}</span></a>'
45835 //                );
45836 //                
45837 //            }
45838
45839
45840             var template = tpl || this.tabTpl || false;
45841             
45842             if(!template){
45843                 template =  new Roo.Template(
45844                         Roo.bootstrap.version == 4 ? 
45845                             (
45846                                 '<a class="nav-link" href="#" unselectable="on"' +
45847                                      (this.disableTooltips ? '' : ' title="{text}"') +
45848                                      ' >{text}</a>'
45849                             ) : (
45850                                 '<a class="nav-link" href="#">' +
45851                                 '<span unselectable="on"' +
45852                                          (this.disableTooltips ? '' : ' title="{text}"') +
45853                                     ' >{text}</span></a>'
45854                             )
45855                 );
45856             }
45857             
45858             switch (typeof(template)) {
45859                 case 'object' :
45860                     break;
45861                 case 'string' :
45862                     template = new Roo.Template(template);
45863                     break;
45864                 default :
45865                     break;
45866             }
45867             
45868             var el = template.overwrite(td, {"text": text});
45869             
45870             var inner = el.getElementsByTagName("span")[0];
45871             
45872             return {"el": el, "inner": inner};
45873             
45874     }
45875         
45876     
45877 });
45878
45879 /**
45880  * @class Roo.TabPanelItem
45881  * @extends Roo.util.Observable
45882  * Represents an individual item (tab plus body) in a TabPanel.
45883  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
45884  * @param {String} id The id of this TabPanelItem
45885  * @param {String} text The text for the tab of this TabPanelItem
45886  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
45887  */
45888 Roo.bootstrap.panel.TabItem = function(config){
45889     /**
45890      * The {@link Roo.TabPanel} this TabPanelItem belongs to
45891      * @type Roo.TabPanel
45892      */
45893     this.tabPanel = config.panel;
45894     /**
45895      * The id for this TabPanelItem
45896      * @type String
45897      */
45898     this.id = config.id;
45899     /** @private */
45900     this.disabled = false;
45901     /** @private */
45902     this.text = config.text;
45903     /** @private */
45904     this.loaded = false;
45905     this.closable = config.closable;
45906
45907     /**
45908      * The body element for this TabPanelItem.
45909      * @type Roo.Element
45910      */
45911     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
45912     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
45913     this.bodyEl.setStyle("display", "block");
45914     this.bodyEl.setStyle("zoom", "1");
45915     //this.hideAction();
45916
45917     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
45918     /** @private */
45919     this.el = Roo.get(els.el);
45920     this.inner = Roo.get(els.inner, true);
45921      this.textEl = Roo.bootstrap.version == 4 ?
45922         this.el : Roo.get(this.el.dom.firstChild, true);
45923
45924     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
45925     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
45926
45927     
45928 //    this.el.on("mousedown", this.onTabMouseDown, this);
45929     this.el.on("click", this.onTabClick, this);
45930     /** @private */
45931     if(config.closable){
45932         var c = Roo.get(els.close, true);
45933         c.dom.title = this.closeText;
45934         c.addClassOnOver("close-over");
45935         c.on("click", this.closeClick, this);
45936      }
45937
45938     this.addEvents({
45939          /**
45940          * @event activate
45941          * Fires when this tab becomes the active tab.
45942          * @param {Roo.TabPanel} tabPanel The parent TabPanel
45943          * @param {Roo.TabPanelItem} this
45944          */
45945         "activate": true,
45946         /**
45947          * @event beforeclose
45948          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
45949          * @param {Roo.TabPanelItem} this
45950          * @param {Object} e Set cancel to true on this object to cancel the close.
45951          */
45952         "beforeclose": true,
45953         /**
45954          * @event close
45955          * Fires when this tab is closed.
45956          * @param {Roo.TabPanelItem} this
45957          */
45958          "close": true,
45959         /**
45960          * @event deactivate
45961          * Fires when this tab is no longer the active tab.
45962          * @param {Roo.TabPanel} tabPanel The parent TabPanel
45963          * @param {Roo.TabPanelItem} this
45964          */
45965          "deactivate" : true
45966     });
45967     this.hidden = false;
45968
45969     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
45970 };
45971
45972 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
45973            {
45974     purgeListeners : function(){
45975        Roo.util.Observable.prototype.purgeListeners.call(this);
45976        this.el.removeAllListeners();
45977     },
45978     /**
45979      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
45980      */
45981     show : function(){
45982         this.status_node.addClass("active");
45983         this.showAction();
45984         if(Roo.isOpera){
45985             this.tabPanel.stripWrap.repaint();
45986         }
45987         this.fireEvent("activate", this.tabPanel, this);
45988     },
45989
45990     /**
45991      * Returns true if this tab is the active tab.
45992      * @return {Boolean}
45993      */
45994     isActive : function(){
45995         return this.tabPanel.getActiveTab() == this;
45996     },
45997
45998     /**
45999      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46000      */
46001     hide : function(){
46002         this.status_node.removeClass("active");
46003         this.hideAction();
46004         this.fireEvent("deactivate", this.tabPanel, this);
46005     },
46006
46007     hideAction : function(){
46008         this.bodyEl.hide();
46009         this.bodyEl.setStyle("position", "absolute");
46010         this.bodyEl.setLeft("-20000px");
46011         this.bodyEl.setTop("-20000px");
46012     },
46013
46014     showAction : function(){
46015         this.bodyEl.setStyle("position", "relative");
46016         this.bodyEl.setTop("");
46017         this.bodyEl.setLeft("");
46018         this.bodyEl.show();
46019     },
46020
46021     /**
46022      * Set the tooltip for the tab.
46023      * @param {String} tooltip The tab's tooltip
46024      */
46025     setTooltip : function(text){
46026         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46027             this.textEl.dom.qtip = text;
46028             this.textEl.dom.removeAttribute('title');
46029         }else{
46030             this.textEl.dom.title = text;
46031         }
46032     },
46033
46034     onTabClick : function(e){
46035         e.preventDefault();
46036         this.tabPanel.activate(this.id);
46037     },
46038
46039     onTabMouseDown : function(e){
46040         e.preventDefault();
46041         this.tabPanel.activate(this.id);
46042     },
46043 /*
46044     getWidth : function(){
46045         return this.inner.getWidth();
46046     },
46047
46048     setWidth : function(width){
46049         var iwidth = width - this.linode.getPadding("lr");
46050         this.inner.setWidth(iwidth);
46051         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46052         this.linode.setWidth(width);
46053     },
46054 */
46055     /**
46056      * Show or hide the tab
46057      * @param {Boolean} hidden True to hide or false to show.
46058      */
46059     setHidden : function(hidden){
46060         this.hidden = hidden;
46061         this.linode.setStyle("display", hidden ? "none" : "");
46062     },
46063
46064     /**
46065      * Returns true if this tab is "hidden"
46066      * @return {Boolean}
46067      */
46068     isHidden : function(){
46069         return this.hidden;
46070     },
46071
46072     /**
46073      * Returns the text for this tab
46074      * @return {String}
46075      */
46076     getText : function(){
46077         return this.text;
46078     },
46079     /*
46080     autoSize : function(){
46081         //this.el.beginMeasure();
46082         this.textEl.setWidth(1);
46083         /*
46084          *  #2804 [new] Tabs in Roojs
46085          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46086          */
46087         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46088         //this.el.endMeasure();
46089     //},
46090
46091     /**
46092      * Sets the text for the tab (Note: this also sets the tooltip text)
46093      * @param {String} text The tab's text and tooltip
46094      */
46095     setText : function(text){
46096         this.text = text;
46097         this.textEl.update(text);
46098         this.setTooltip(text);
46099         //if(!this.tabPanel.resizeTabs){
46100         //    this.autoSize();
46101         //}
46102     },
46103     /**
46104      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46105      */
46106     activate : function(){
46107         this.tabPanel.activate(this.id);
46108     },
46109
46110     /**
46111      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46112      */
46113     disable : function(){
46114         if(this.tabPanel.active != this){
46115             this.disabled = true;
46116             this.status_node.addClass("disabled");
46117         }
46118     },
46119
46120     /**
46121      * Enables this TabPanelItem if it was previously disabled.
46122      */
46123     enable : function(){
46124         this.disabled = false;
46125         this.status_node.removeClass("disabled");
46126     },
46127
46128     /**
46129      * Sets the content for this TabPanelItem.
46130      * @param {String} content The content
46131      * @param {Boolean} loadScripts true to look for and load scripts
46132      */
46133     setContent : function(content, loadScripts){
46134         this.bodyEl.update(content, loadScripts);
46135     },
46136
46137     /**
46138      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
46139      * @return {Roo.UpdateManager} The UpdateManager
46140      */
46141     getUpdateManager : function(){
46142         return this.bodyEl.getUpdateManager();
46143     },
46144
46145     /**
46146      * Set a URL to be used to load the content for this TabPanelItem.
46147      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
46148      * @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)
46149      * @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)
46150      * @return {Roo.UpdateManager} The UpdateManager
46151      */
46152     setUrl : function(url, params, loadOnce){
46153         if(this.refreshDelegate){
46154             this.un('activate', this.refreshDelegate);
46155         }
46156         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46157         this.on("activate", this.refreshDelegate);
46158         return this.bodyEl.getUpdateManager();
46159     },
46160
46161     /** @private */
46162     _handleRefresh : function(url, params, loadOnce){
46163         if(!loadOnce || !this.loaded){
46164             var updater = this.bodyEl.getUpdateManager();
46165             updater.update(url, params, this._setLoaded.createDelegate(this));
46166         }
46167     },
46168
46169     /**
46170      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
46171      *   Will fail silently if the setUrl method has not been called.
46172      *   This does not activate the panel, just updates its content.
46173      */
46174     refresh : function(){
46175         if(this.refreshDelegate){
46176            this.loaded = false;
46177            this.refreshDelegate();
46178         }
46179     },
46180
46181     /** @private */
46182     _setLoaded : function(){
46183         this.loaded = true;
46184     },
46185
46186     /** @private */
46187     closeClick : function(e){
46188         var o = {};
46189         e.stopEvent();
46190         this.fireEvent("beforeclose", this, o);
46191         if(o.cancel !== true){
46192             this.tabPanel.removeTab(this.id);
46193         }
46194     },
46195     /**
46196      * The text displayed in the tooltip for the close icon.
46197      * @type String
46198      */
46199     closeText : "Close this tab"
46200 });
46201 /**
46202 *    This script refer to:
46203 *    Title: International Telephone Input
46204 *    Author: Jack O'Connor
46205 *    Code version:  v12.1.12
46206 *    Availability: https://github.com/jackocnr/intl-tel-input.git
46207 **/
46208
46209 Roo.bootstrap.form.PhoneInputData = function() {
46210     var d = [
46211       [
46212         "Afghanistan (‫افغانستان‬‎)",
46213         "af",
46214         "93"
46215       ],
46216       [
46217         "Albania (Shqipëri)",
46218         "al",
46219         "355"
46220       ],
46221       [
46222         "Algeria (‫الجزائر‬‎)",
46223         "dz",
46224         "213"
46225       ],
46226       [
46227         "American Samoa",
46228         "as",
46229         "1684"
46230       ],
46231       [
46232         "Andorra",
46233         "ad",
46234         "376"
46235       ],
46236       [
46237         "Angola",
46238         "ao",
46239         "244"
46240       ],
46241       [
46242         "Anguilla",
46243         "ai",
46244         "1264"
46245       ],
46246       [
46247         "Antigua and Barbuda",
46248         "ag",
46249         "1268"
46250       ],
46251       [
46252         "Argentina",
46253         "ar",
46254         "54"
46255       ],
46256       [
46257         "Armenia (Հայաստան)",
46258         "am",
46259         "374"
46260       ],
46261       [
46262         "Aruba",
46263         "aw",
46264         "297"
46265       ],
46266       [
46267         "Australia",
46268         "au",
46269         "61",
46270         0
46271       ],
46272       [
46273         "Austria (Österreich)",
46274         "at",
46275         "43"
46276       ],
46277       [
46278         "Azerbaijan (Azərbaycan)",
46279         "az",
46280         "994"
46281       ],
46282       [
46283         "Bahamas",
46284         "bs",
46285         "1242"
46286       ],
46287       [
46288         "Bahrain (‫البحرين‬‎)",
46289         "bh",
46290         "973"
46291       ],
46292       [
46293         "Bangladesh (বাংলাদেশ)",
46294         "bd",
46295         "880"
46296       ],
46297       [
46298         "Barbados",
46299         "bb",
46300         "1246"
46301       ],
46302       [
46303         "Belarus (Беларусь)",
46304         "by",
46305         "375"
46306       ],
46307       [
46308         "Belgium (België)",
46309         "be",
46310         "32"
46311       ],
46312       [
46313         "Belize",
46314         "bz",
46315         "501"
46316       ],
46317       [
46318         "Benin (Bénin)",
46319         "bj",
46320         "229"
46321       ],
46322       [
46323         "Bermuda",
46324         "bm",
46325         "1441"
46326       ],
46327       [
46328         "Bhutan (འབྲུག)",
46329         "bt",
46330         "975"
46331       ],
46332       [
46333         "Bolivia",
46334         "bo",
46335         "591"
46336       ],
46337       [
46338         "Bosnia and Herzegovina (Босна и Херцеговина)",
46339         "ba",
46340         "387"
46341       ],
46342       [
46343         "Botswana",
46344         "bw",
46345         "267"
46346       ],
46347       [
46348         "Brazil (Brasil)",
46349         "br",
46350         "55"
46351       ],
46352       [
46353         "British Indian Ocean Territory",
46354         "io",
46355         "246"
46356       ],
46357       [
46358         "British Virgin Islands",
46359         "vg",
46360         "1284"
46361       ],
46362       [
46363         "Brunei",
46364         "bn",
46365         "673"
46366       ],
46367       [
46368         "Bulgaria (България)",
46369         "bg",
46370         "359"
46371       ],
46372       [
46373         "Burkina Faso",
46374         "bf",
46375         "226"
46376       ],
46377       [
46378         "Burundi (Uburundi)",
46379         "bi",
46380         "257"
46381       ],
46382       [
46383         "Cambodia (កម្ពុជា)",
46384         "kh",
46385         "855"
46386       ],
46387       [
46388         "Cameroon (Cameroun)",
46389         "cm",
46390         "237"
46391       ],
46392       [
46393         "Canada",
46394         "ca",
46395         "1",
46396         1,
46397         ["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"]
46398       ],
46399       [
46400         "Cape Verde (Kabu Verdi)",
46401         "cv",
46402         "238"
46403       ],
46404       [
46405         "Caribbean Netherlands",
46406         "bq",
46407         "599",
46408         1
46409       ],
46410       [
46411         "Cayman Islands",
46412         "ky",
46413         "1345"
46414       ],
46415       [
46416         "Central African Republic (République centrafricaine)",
46417         "cf",
46418         "236"
46419       ],
46420       [
46421         "Chad (Tchad)",
46422         "td",
46423         "235"
46424       ],
46425       [
46426         "Chile",
46427         "cl",
46428         "56"
46429       ],
46430       [
46431         "China (中国)",
46432         "cn",
46433         "86"
46434       ],
46435       [
46436         "Christmas Island",
46437         "cx",
46438         "61",
46439         2
46440       ],
46441       [
46442         "Cocos (Keeling) Islands",
46443         "cc",
46444         "61",
46445         1
46446       ],
46447       [
46448         "Colombia",
46449         "co",
46450         "57"
46451       ],
46452       [
46453         "Comoros (‫جزر القمر‬‎)",
46454         "km",
46455         "269"
46456       ],
46457       [
46458         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
46459         "cd",
46460         "243"
46461       ],
46462       [
46463         "Congo (Republic) (Congo-Brazzaville)",
46464         "cg",
46465         "242"
46466       ],
46467       [
46468         "Cook Islands",
46469         "ck",
46470         "682"
46471       ],
46472       [
46473         "Costa Rica",
46474         "cr",
46475         "506"
46476       ],
46477       [
46478         "Côte d’Ivoire",
46479         "ci",
46480         "225"
46481       ],
46482       [
46483         "Croatia (Hrvatska)",
46484         "hr",
46485         "385"
46486       ],
46487       [
46488         "Cuba",
46489         "cu",
46490         "53"
46491       ],
46492       [
46493         "Curaçao",
46494         "cw",
46495         "599",
46496         0
46497       ],
46498       [
46499         "Cyprus (Κύπρος)",
46500         "cy",
46501         "357"
46502       ],
46503       [
46504         "Czech Republic (Česká republika)",
46505         "cz",
46506         "420"
46507       ],
46508       [
46509         "Denmark (Danmark)",
46510         "dk",
46511         "45"
46512       ],
46513       [
46514         "Djibouti",
46515         "dj",
46516         "253"
46517       ],
46518       [
46519         "Dominica",
46520         "dm",
46521         "1767"
46522       ],
46523       [
46524         "Dominican Republic (República Dominicana)",
46525         "do",
46526         "1",
46527         2,
46528         ["809", "829", "849"]
46529       ],
46530       [
46531         "Ecuador",
46532         "ec",
46533         "593"
46534       ],
46535       [
46536         "Egypt (‫مصر‬‎)",
46537         "eg",
46538         "20"
46539       ],
46540       [
46541         "El Salvador",
46542         "sv",
46543         "503"
46544       ],
46545       [
46546         "Equatorial Guinea (Guinea Ecuatorial)",
46547         "gq",
46548         "240"
46549       ],
46550       [
46551         "Eritrea",
46552         "er",
46553         "291"
46554       ],
46555       [
46556         "Estonia (Eesti)",
46557         "ee",
46558         "372"
46559       ],
46560       [
46561         "Ethiopia",
46562         "et",
46563         "251"
46564       ],
46565       [
46566         "Falkland Islands (Islas Malvinas)",
46567         "fk",
46568         "500"
46569       ],
46570       [
46571         "Faroe Islands (Føroyar)",
46572         "fo",
46573         "298"
46574       ],
46575       [
46576         "Fiji",
46577         "fj",
46578         "679"
46579       ],
46580       [
46581         "Finland (Suomi)",
46582         "fi",
46583         "358",
46584         0
46585       ],
46586       [
46587         "France",
46588         "fr",
46589         "33"
46590       ],
46591       [
46592         "French Guiana (Guyane française)",
46593         "gf",
46594         "594"
46595       ],
46596       [
46597         "French Polynesia (Polynésie française)",
46598         "pf",
46599         "689"
46600       ],
46601       [
46602         "Gabon",
46603         "ga",
46604         "241"
46605       ],
46606       [
46607         "Gambia",
46608         "gm",
46609         "220"
46610       ],
46611       [
46612         "Georgia (საქართველო)",
46613         "ge",
46614         "995"
46615       ],
46616       [
46617         "Germany (Deutschland)",
46618         "de",
46619         "49"
46620       ],
46621       [
46622         "Ghana (Gaana)",
46623         "gh",
46624         "233"
46625       ],
46626       [
46627         "Gibraltar",
46628         "gi",
46629         "350"
46630       ],
46631       [
46632         "Greece (Ελλάδα)",
46633         "gr",
46634         "30"
46635       ],
46636       [
46637         "Greenland (Kalaallit Nunaat)",
46638         "gl",
46639         "299"
46640       ],
46641       [
46642         "Grenada",
46643         "gd",
46644         "1473"
46645       ],
46646       [
46647         "Guadeloupe",
46648         "gp",
46649         "590",
46650         0
46651       ],
46652       [
46653         "Guam",
46654         "gu",
46655         "1671"
46656       ],
46657       [
46658         "Guatemala",
46659         "gt",
46660         "502"
46661       ],
46662       [
46663         "Guernsey",
46664         "gg",
46665         "44",
46666         1
46667       ],
46668       [
46669         "Guinea (Guinée)",
46670         "gn",
46671         "224"
46672       ],
46673       [
46674         "Guinea-Bissau (Guiné Bissau)",
46675         "gw",
46676         "245"
46677       ],
46678       [
46679         "Guyana",
46680         "gy",
46681         "592"
46682       ],
46683       [
46684         "Haiti",
46685         "ht",
46686         "509"
46687       ],
46688       [
46689         "Honduras",
46690         "hn",
46691         "504"
46692       ],
46693       [
46694         "Hong Kong (香港)",
46695         "hk",
46696         "852"
46697       ],
46698       [
46699         "Hungary (Magyarország)",
46700         "hu",
46701         "36"
46702       ],
46703       [
46704         "Iceland (Ísland)",
46705         "is",
46706         "354"
46707       ],
46708       [
46709         "India (भारत)",
46710         "in",
46711         "91"
46712       ],
46713       [
46714         "Indonesia",
46715         "id",
46716         "62"
46717       ],
46718       [
46719         "Iran (‫ایران‬‎)",
46720         "ir",
46721         "98"
46722       ],
46723       [
46724         "Iraq (‫العراق‬‎)",
46725         "iq",
46726         "964"
46727       ],
46728       [
46729         "Ireland",
46730         "ie",
46731         "353"
46732       ],
46733       [
46734         "Isle of Man",
46735         "im",
46736         "44",
46737         2
46738       ],
46739       [
46740         "Israel (‫ישראל‬‎)",
46741         "il",
46742         "972"
46743       ],
46744       [
46745         "Italy (Italia)",
46746         "it",
46747         "39",
46748         0
46749       ],
46750       [
46751         "Jamaica",
46752         "jm",
46753         "1876"
46754       ],
46755       [
46756         "Japan (日本)",
46757         "jp",
46758         "81"
46759       ],
46760       [
46761         "Jersey",
46762         "je",
46763         "44",
46764         3
46765       ],
46766       [
46767         "Jordan (‫الأردن‬‎)",
46768         "jo",
46769         "962"
46770       ],
46771       [
46772         "Kazakhstan (Казахстан)",
46773         "kz",
46774         "7",
46775         1
46776       ],
46777       [
46778         "Kenya",
46779         "ke",
46780         "254"
46781       ],
46782       [
46783         "Kiribati",
46784         "ki",
46785         "686"
46786       ],
46787       [
46788         "Kosovo",
46789         "xk",
46790         "383"
46791       ],
46792       [
46793         "Kuwait (‫الكويت‬‎)",
46794         "kw",
46795         "965"
46796       ],
46797       [
46798         "Kyrgyzstan (Кыргызстан)",
46799         "kg",
46800         "996"
46801       ],
46802       [
46803         "Laos (ລາວ)",
46804         "la",
46805         "856"
46806       ],
46807       [
46808         "Latvia (Latvija)",
46809         "lv",
46810         "371"
46811       ],
46812       [
46813         "Lebanon (‫لبنان‬‎)",
46814         "lb",
46815         "961"
46816       ],
46817       [
46818         "Lesotho",
46819         "ls",
46820         "266"
46821       ],
46822       [
46823         "Liberia",
46824         "lr",
46825         "231"
46826       ],
46827       [
46828         "Libya (‫ليبيا‬‎)",
46829         "ly",
46830         "218"
46831       ],
46832       [
46833         "Liechtenstein",
46834         "li",
46835         "423"
46836       ],
46837       [
46838         "Lithuania (Lietuva)",
46839         "lt",
46840         "370"
46841       ],
46842       [
46843         "Luxembourg",
46844         "lu",
46845         "352"
46846       ],
46847       [
46848         "Macau (澳門)",
46849         "mo",
46850         "853"
46851       ],
46852       [
46853         "Macedonia (FYROM) (Македонија)",
46854         "mk",
46855         "389"
46856       ],
46857       [
46858         "Madagascar (Madagasikara)",
46859         "mg",
46860         "261"
46861       ],
46862       [
46863         "Malawi",
46864         "mw",
46865         "265"
46866       ],
46867       [
46868         "Malaysia",
46869         "my",
46870         "60"
46871       ],
46872       [
46873         "Maldives",
46874         "mv",
46875         "960"
46876       ],
46877       [
46878         "Mali",
46879         "ml",
46880         "223"
46881       ],
46882       [
46883         "Malta",
46884         "mt",
46885         "356"
46886       ],
46887       [
46888         "Marshall Islands",
46889         "mh",
46890         "692"
46891       ],
46892       [
46893         "Martinique",
46894         "mq",
46895         "596"
46896       ],
46897       [
46898         "Mauritania (‫موريتانيا‬‎)",
46899         "mr",
46900         "222"
46901       ],
46902       [
46903         "Mauritius (Moris)",
46904         "mu",
46905         "230"
46906       ],
46907       [
46908         "Mayotte",
46909         "yt",
46910         "262",
46911         1
46912       ],
46913       [
46914         "Mexico (México)",
46915         "mx",
46916         "52"
46917       ],
46918       [
46919         "Micronesia",
46920         "fm",
46921         "691"
46922       ],
46923       [
46924         "Moldova (Republica Moldova)",
46925         "md",
46926         "373"
46927       ],
46928       [
46929         "Monaco",
46930         "mc",
46931         "377"
46932       ],
46933       [
46934         "Mongolia (Монгол)",
46935         "mn",
46936         "976"
46937       ],
46938       [
46939         "Montenegro (Crna Gora)",
46940         "me",
46941         "382"
46942       ],
46943       [
46944         "Montserrat",
46945         "ms",
46946         "1664"
46947       ],
46948       [
46949         "Morocco (‫المغرب‬‎)",
46950         "ma",
46951         "212",
46952         0
46953       ],
46954       [
46955         "Mozambique (Moçambique)",
46956         "mz",
46957         "258"
46958       ],
46959       [
46960         "Myanmar (Burma) (မြန်မာ)",
46961         "mm",
46962         "95"
46963       ],
46964       [
46965         "Namibia (Namibië)",
46966         "na",
46967         "264"
46968       ],
46969       [
46970         "Nauru",
46971         "nr",
46972         "674"
46973       ],
46974       [
46975         "Nepal (नेपाल)",
46976         "np",
46977         "977"
46978       ],
46979       [
46980         "Netherlands (Nederland)",
46981         "nl",
46982         "31"
46983       ],
46984       [
46985         "New Caledonia (Nouvelle-Calédonie)",
46986         "nc",
46987         "687"
46988       ],
46989       [
46990         "New Zealand",
46991         "nz",
46992         "64"
46993       ],
46994       [
46995         "Nicaragua",
46996         "ni",
46997         "505"
46998       ],
46999       [
47000         "Niger (Nijar)",
47001         "ne",
47002         "227"
47003       ],
47004       [
47005         "Nigeria",
47006         "ng",
47007         "234"
47008       ],
47009       [
47010         "Niue",
47011         "nu",
47012         "683"
47013       ],
47014       [
47015         "Norfolk Island",
47016         "nf",
47017         "672"
47018       ],
47019       [
47020         "North Korea (조선 민주주의 인민 공화국)",
47021         "kp",
47022         "850"
47023       ],
47024       [
47025         "Northern Mariana Islands",
47026         "mp",
47027         "1670"
47028       ],
47029       [
47030         "Norway (Norge)",
47031         "no",
47032         "47",
47033         0
47034       ],
47035       [
47036         "Oman (‫عُمان‬‎)",
47037         "om",
47038         "968"
47039       ],
47040       [
47041         "Pakistan (‫پاکستان‬‎)",
47042         "pk",
47043         "92"
47044       ],
47045       [
47046         "Palau",
47047         "pw",
47048         "680"
47049       ],
47050       [
47051         "Palestine (‫فلسطين‬‎)",
47052         "ps",
47053         "970"
47054       ],
47055       [
47056         "Panama (Panamá)",
47057         "pa",
47058         "507"
47059       ],
47060       [
47061         "Papua New Guinea",
47062         "pg",
47063         "675"
47064       ],
47065       [
47066         "Paraguay",
47067         "py",
47068         "595"
47069       ],
47070       [
47071         "Peru (Perú)",
47072         "pe",
47073         "51"
47074       ],
47075       [
47076         "Philippines",
47077         "ph",
47078         "63"
47079       ],
47080       [
47081         "Poland (Polska)",
47082         "pl",
47083         "48"
47084       ],
47085       [
47086         "Portugal",
47087         "pt",
47088         "351"
47089       ],
47090       [
47091         "Puerto Rico",
47092         "pr",
47093         "1",
47094         3,
47095         ["787", "939"]
47096       ],
47097       [
47098         "Qatar (‫قطر‬‎)",
47099         "qa",
47100         "974"
47101       ],
47102       [
47103         "Réunion (La Réunion)",
47104         "re",
47105         "262",
47106         0
47107       ],
47108       [
47109         "Romania (România)",
47110         "ro",
47111         "40"
47112       ],
47113       [
47114         "Russia (Россия)",
47115         "ru",
47116         "7",
47117         0
47118       ],
47119       [
47120         "Rwanda",
47121         "rw",
47122         "250"
47123       ],
47124       [
47125         "Saint Barthélemy",
47126         "bl",
47127         "590",
47128         1
47129       ],
47130       [
47131         "Saint Helena",
47132         "sh",
47133         "290"
47134       ],
47135       [
47136         "Saint Kitts and Nevis",
47137         "kn",
47138         "1869"
47139       ],
47140       [
47141         "Saint Lucia",
47142         "lc",
47143         "1758"
47144       ],
47145       [
47146         "Saint Martin (Saint-Martin (partie française))",
47147         "mf",
47148         "590",
47149         2
47150       ],
47151       [
47152         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
47153         "pm",
47154         "508"
47155       ],
47156       [
47157         "Saint Vincent and the Grenadines",
47158         "vc",
47159         "1784"
47160       ],
47161       [
47162         "Samoa",
47163         "ws",
47164         "685"
47165       ],
47166       [
47167         "San Marino",
47168         "sm",
47169         "378"
47170       ],
47171       [
47172         "São Tomé and Príncipe (São Tomé e Príncipe)",
47173         "st",
47174         "239"
47175       ],
47176       [
47177         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
47178         "sa",
47179         "966"
47180       ],
47181       [
47182         "Senegal (Sénégal)",
47183         "sn",
47184         "221"
47185       ],
47186       [
47187         "Serbia (Србија)",
47188         "rs",
47189         "381"
47190       ],
47191       [
47192         "Seychelles",
47193         "sc",
47194         "248"
47195       ],
47196       [
47197         "Sierra Leone",
47198         "sl",
47199         "232"
47200       ],
47201       [
47202         "Singapore",
47203         "sg",
47204         "65"
47205       ],
47206       [
47207         "Sint Maarten",
47208         "sx",
47209         "1721"
47210       ],
47211       [
47212         "Slovakia (Slovensko)",
47213         "sk",
47214         "421"
47215       ],
47216       [
47217         "Slovenia (Slovenija)",
47218         "si",
47219         "386"
47220       ],
47221       [
47222         "Solomon Islands",
47223         "sb",
47224         "677"
47225       ],
47226       [
47227         "Somalia (Soomaaliya)",
47228         "so",
47229         "252"
47230       ],
47231       [
47232         "South Africa",
47233         "za",
47234         "27"
47235       ],
47236       [
47237         "South Korea (대한민국)",
47238         "kr",
47239         "82"
47240       ],
47241       [
47242         "South Sudan (‫جنوب السودان‬‎)",
47243         "ss",
47244         "211"
47245       ],
47246       [
47247         "Spain (España)",
47248         "es",
47249         "34"
47250       ],
47251       [
47252         "Sri Lanka (ශ්‍රී ලංකාව)",
47253         "lk",
47254         "94"
47255       ],
47256       [
47257         "Sudan (‫السودان‬‎)",
47258         "sd",
47259         "249"
47260       ],
47261       [
47262         "Suriname",
47263         "sr",
47264         "597"
47265       ],
47266       [
47267         "Svalbard and Jan Mayen",
47268         "sj",
47269         "47",
47270         1
47271       ],
47272       [
47273         "Swaziland",
47274         "sz",
47275         "268"
47276       ],
47277       [
47278         "Sweden (Sverige)",
47279         "se",
47280         "46"
47281       ],
47282       [
47283         "Switzerland (Schweiz)",
47284         "ch",
47285         "41"
47286       ],
47287       [
47288         "Syria (‫سوريا‬‎)",
47289         "sy",
47290         "963"
47291       ],
47292       [
47293         "Taiwan (台灣)",
47294         "tw",
47295         "886"
47296       ],
47297       [
47298         "Tajikistan",
47299         "tj",
47300         "992"
47301       ],
47302       [
47303         "Tanzania",
47304         "tz",
47305         "255"
47306       ],
47307       [
47308         "Thailand (ไทย)",
47309         "th",
47310         "66"
47311       ],
47312       [
47313         "Timor-Leste",
47314         "tl",
47315         "670"
47316       ],
47317       [
47318         "Togo",
47319         "tg",
47320         "228"
47321       ],
47322       [
47323         "Tokelau",
47324         "tk",
47325         "690"
47326       ],
47327       [
47328         "Tonga",
47329         "to",
47330         "676"
47331       ],
47332       [
47333         "Trinidad and Tobago",
47334         "tt",
47335         "1868"
47336       ],
47337       [
47338         "Tunisia (‫تونس‬‎)",
47339         "tn",
47340         "216"
47341       ],
47342       [
47343         "Turkey (Türkiye)",
47344         "tr",
47345         "90"
47346       ],
47347       [
47348         "Turkmenistan",
47349         "tm",
47350         "993"
47351       ],
47352       [
47353         "Turks and Caicos Islands",
47354         "tc",
47355         "1649"
47356       ],
47357       [
47358         "Tuvalu",
47359         "tv",
47360         "688"
47361       ],
47362       [
47363         "U.S. Virgin Islands",
47364         "vi",
47365         "1340"
47366       ],
47367       [
47368         "Uganda",
47369         "ug",
47370         "256"
47371       ],
47372       [
47373         "Ukraine (Україна)",
47374         "ua",
47375         "380"
47376       ],
47377       [
47378         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
47379         "ae",
47380         "971"
47381       ],
47382       [
47383         "United Kingdom",
47384         "gb",
47385         "44",
47386         0
47387       ],
47388       [
47389         "United States",
47390         "us",
47391         "1",
47392         0
47393       ],
47394       [
47395         "Uruguay",
47396         "uy",
47397         "598"
47398       ],
47399       [
47400         "Uzbekistan (Oʻzbekiston)",
47401         "uz",
47402         "998"
47403       ],
47404       [
47405         "Vanuatu",
47406         "vu",
47407         "678"
47408       ],
47409       [
47410         "Vatican City (Città del Vaticano)",
47411         "va",
47412         "39",
47413         1
47414       ],
47415       [
47416         "Venezuela",
47417         "ve",
47418         "58"
47419       ],
47420       [
47421         "Vietnam (Việt Nam)",
47422         "vn",
47423         "84"
47424       ],
47425       [
47426         "Wallis and Futuna (Wallis-et-Futuna)",
47427         "wf",
47428         "681"
47429       ],
47430       [
47431         "Western Sahara (‫الصحراء الغربية‬‎)",
47432         "eh",
47433         "212",
47434         1
47435       ],
47436       [
47437         "Yemen (‫اليمن‬‎)",
47438         "ye",
47439         "967"
47440       ],
47441       [
47442         "Zambia",
47443         "zm",
47444         "260"
47445       ],
47446       [
47447         "Zimbabwe",
47448         "zw",
47449         "263"
47450       ],
47451       [
47452         "Åland Islands",
47453         "ax",
47454         "358",
47455         1
47456       ]
47457   ];
47458   
47459   return d;
47460 }/**
47461 *    This script refer to:
47462 *    Title: International Telephone Input
47463 *    Author: Jack O'Connor
47464 *    Code version:  v12.1.12
47465 *    Availability: https://github.com/jackocnr/intl-tel-input.git
47466 **/
47467
47468 /**
47469  * @class Roo.bootstrap.form.PhoneInput
47470  * @extends Roo.bootstrap.form.TriggerField
47471  * An input with International dial-code selection
47472  
47473  * @cfg {String} defaultDialCode default '+852'
47474  * @cfg {Array} preferedCountries default []
47475   
47476  * @constructor
47477  * Create a new PhoneInput.
47478  * @param {Object} config Configuration options
47479  */
47480
47481 Roo.bootstrap.form.PhoneInput = function(config) {
47482     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
47483 };
47484
47485 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
47486         /**
47487         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
47488         */
47489         listWidth: undefined,
47490         
47491         selectedClass: 'active',
47492         
47493         invalidClass : "has-warning",
47494         
47495         validClass: 'has-success',
47496         
47497         allowed: '0123456789',
47498         
47499         max_length: 15,
47500         
47501         /**
47502          * @cfg {String} defaultDialCode The default dial code when initializing the input
47503          */
47504         defaultDialCode: '+852',
47505         
47506         /**
47507          * @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
47508          */
47509         preferedCountries: false,
47510         
47511         getAutoCreate : function()
47512         {
47513             var data = Roo.bootstrap.form.PhoneInputData();
47514             var align = this.labelAlign || this.parentLabelAlign();
47515             var id = Roo.id();
47516             
47517             this.allCountries = [];
47518             this.dialCodeMapping = [];
47519             
47520             for (var i = 0; i < data.length; i++) {
47521               var c = data[i];
47522               this.allCountries[i] = {
47523                 name: c[0],
47524                 iso2: c[1],
47525                 dialCode: c[2],
47526                 priority: c[3] || 0,
47527                 areaCodes: c[4] || null
47528               };
47529               this.dialCodeMapping[c[2]] = {
47530                   name: c[0],
47531                   iso2: c[1],
47532                   priority: c[3] || 0,
47533                   areaCodes: c[4] || null
47534               };
47535             }
47536             
47537             var cfg = {
47538                 cls: 'form-group',
47539                 cn: []
47540             };
47541             
47542             var input =  {
47543                 tag: 'input',
47544                 id : id,
47545                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
47546                 maxlength: this.max_length,
47547                 cls : 'form-control tel-input',
47548                 autocomplete: 'new-password'
47549             };
47550             
47551             var hiddenInput = {
47552                 tag: 'input',
47553                 type: 'hidden',
47554                 cls: 'hidden-tel-input'
47555             };
47556             
47557             if (this.name) {
47558                 hiddenInput.name = this.name;
47559             }
47560             
47561             if (this.disabled) {
47562                 input.disabled = true;
47563             }
47564             
47565             var flag_container = {
47566                 tag: 'div',
47567                 cls: 'flag-box',
47568                 cn: [
47569                     {
47570                         tag: 'div',
47571                         cls: 'flag'
47572                     },
47573                     {
47574                         tag: 'div',
47575                         cls: 'caret'
47576                     }
47577                 ]
47578             };
47579             
47580             var box = {
47581                 tag: 'div',
47582                 cls: this.hasFeedback ? 'has-feedback' : '',
47583                 cn: [
47584                     hiddenInput,
47585                     input,
47586                     {
47587                         tag: 'input',
47588                         cls: 'dial-code-holder',
47589                         disabled: true
47590                     }
47591                 ]
47592             };
47593             
47594             var container = {
47595                 cls: 'roo-select2-container input-group',
47596                 cn: [
47597                     flag_container,
47598                     box
47599                 ]
47600             };
47601             
47602             if (this.fieldLabel.length) {
47603                 var indicator = {
47604                     tag: 'i',
47605                     tooltip: 'This field is required'
47606                 };
47607                 
47608                 var label = {
47609                     tag: 'label',
47610                     'for':  id,
47611                     cls: 'control-label',
47612                     cn: []
47613                 };
47614                 
47615                 var label_text = {
47616                     tag: 'span',
47617                     html: this.fieldLabel
47618                 };
47619                 
47620                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
47621                 label.cn = [
47622                     indicator,
47623                     label_text
47624                 ];
47625                 
47626                 if(this.indicatorpos == 'right') {
47627                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
47628                     label.cn = [
47629                         label_text,
47630                         indicator
47631                     ];
47632                 }
47633                 
47634                 if(align == 'left') {
47635                     container = {
47636                         tag: 'div',
47637                         cn: [
47638                             container
47639                         ]
47640                     };
47641                     
47642                     if(this.labelWidth > 12){
47643                         label.style = "width: " + this.labelWidth + 'px';
47644                     }
47645                     if(this.labelWidth < 13 && this.labelmd == 0){
47646                         this.labelmd = this.labelWidth;
47647                     }
47648                     if(this.labellg > 0){
47649                         label.cls += ' col-lg-' + this.labellg;
47650                         input.cls += ' col-lg-' + (12 - this.labellg);
47651                     }
47652                     if(this.labelmd > 0){
47653                         label.cls += ' col-md-' + this.labelmd;
47654                         container.cls += ' col-md-' + (12 - this.labelmd);
47655                     }
47656                     if(this.labelsm > 0){
47657                         label.cls += ' col-sm-' + this.labelsm;
47658                         container.cls += ' col-sm-' + (12 - this.labelsm);
47659                     }
47660                     if(this.labelxs > 0){
47661                         label.cls += ' col-xs-' + this.labelxs;
47662                         container.cls += ' col-xs-' + (12 - this.labelxs);
47663                     }
47664                 }
47665             }
47666             
47667             cfg.cn = [
47668                 label,
47669                 container
47670             ];
47671             
47672             var settings = this;
47673             
47674             ['xs','sm','md','lg'].map(function(size){
47675                 if (settings[size]) {
47676                     cfg.cls += ' col-' + size + '-' + settings[size];
47677                 }
47678             });
47679             
47680             this.store = new Roo.data.Store({
47681                 proxy : new Roo.data.MemoryProxy({}),
47682                 reader : new Roo.data.JsonReader({
47683                     fields : [
47684                         {
47685                             'name' : 'name',
47686                             'type' : 'string'
47687                         },
47688                         {
47689                             'name' : 'iso2',
47690                             'type' : 'string'
47691                         },
47692                         {
47693                             'name' : 'dialCode',
47694                             'type' : 'string'
47695                         },
47696                         {
47697                             'name' : 'priority',
47698                             'type' : 'string'
47699                         },
47700                         {
47701                             'name' : 'areaCodes',
47702                             'type' : 'string'
47703                         }
47704                     ]
47705                 })
47706             });
47707             
47708             if(!this.preferedCountries) {
47709                 this.preferedCountries = [
47710                     'hk',
47711                     'gb',
47712                     'us'
47713                 ];
47714             }
47715             
47716             var p = this.preferedCountries.reverse();
47717             
47718             if(p) {
47719                 for (var i = 0; i < p.length; i++) {
47720                     for (var j = 0; j < this.allCountries.length; j++) {
47721                         if(this.allCountries[j].iso2 == p[i]) {
47722                             var t = this.allCountries[j];
47723                             this.allCountries.splice(j,1);
47724                             this.allCountries.unshift(t);
47725                         }
47726                     } 
47727                 }
47728             }
47729             
47730             this.store.proxy.data = {
47731                 success: true,
47732                 data: this.allCountries
47733             };
47734             
47735             return cfg;
47736         },
47737         
47738         initEvents : function()
47739         {
47740             this.createList();
47741             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
47742             
47743             this.indicator = this.indicatorEl();
47744             this.flag = this.flagEl();
47745             this.dialCodeHolder = this.dialCodeHolderEl();
47746             
47747             this.trigger = this.el.select('div.flag-box',true).first();
47748             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
47749             
47750             var _this = this;
47751             
47752             (function(){
47753                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
47754                 _this.list.setWidth(lw);
47755             }).defer(100);
47756             
47757             this.list.on('mouseover', this.onViewOver, this);
47758             this.list.on('mousemove', this.onViewMove, this);
47759             this.inputEl().on("keyup", this.onKeyUp, this);
47760             this.inputEl().on("keypress", this.onKeyPress, this);
47761             
47762             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
47763
47764             this.view = new Roo.View(this.list, this.tpl, {
47765                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
47766             });
47767             
47768             this.view.on('click', this.onViewClick, this);
47769             this.setValue(this.defaultDialCode);
47770         },
47771         
47772         onTriggerClick : function(e)
47773         {
47774             Roo.log('trigger click');
47775             if(this.disabled){
47776                 return;
47777             }
47778             
47779             if(this.isExpanded()){
47780                 this.collapse();
47781                 this.hasFocus = false;
47782             }else {
47783                 this.store.load({});
47784                 this.hasFocus = true;
47785                 this.expand();
47786             }
47787         },
47788         
47789         isExpanded : function()
47790         {
47791             return this.list.isVisible();
47792         },
47793         
47794         collapse : function()
47795         {
47796             if(!this.isExpanded()){
47797                 return;
47798             }
47799             this.list.hide();
47800             Roo.get(document).un('mousedown', this.collapseIf, this);
47801             Roo.get(document).un('mousewheel', this.collapseIf, this);
47802             this.fireEvent('collapse', this);
47803             this.validate();
47804         },
47805         
47806         expand : function()
47807         {
47808             Roo.log('expand');
47809
47810             if(this.isExpanded() || !this.hasFocus){
47811                 return;
47812             }
47813             
47814             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
47815             this.list.setWidth(lw);
47816             
47817             this.list.show();
47818             this.restrictHeight();
47819             
47820             Roo.get(document).on('mousedown', this.collapseIf, this);
47821             Roo.get(document).on('mousewheel', this.collapseIf, this);
47822             
47823             this.fireEvent('expand', this);
47824         },
47825         
47826         restrictHeight : function()
47827         {
47828             this.list.alignTo(this.inputEl(), this.listAlign);
47829             this.list.alignTo(this.inputEl(), this.listAlign);
47830         },
47831         
47832         onViewOver : function(e, t)
47833         {
47834             if(this.inKeyMode){
47835                 return;
47836             }
47837             var item = this.view.findItemFromChild(t);
47838             
47839             if(item){
47840                 var index = this.view.indexOf(item);
47841                 this.select(index, false);
47842             }
47843         },
47844
47845         // private
47846         onViewClick : function(view, doFocus, el, e)
47847         {
47848             var index = this.view.getSelectedIndexes()[0];
47849             
47850             var r = this.store.getAt(index);
47851             
47852             if(r){
47853                 this.onSelect(r, index);
47854             }
47855             if(doFocus !== false && !this.blockFocus){
47856                 this.inputEl().focus();
47857             }
47858         },
47859         
47860         onViewMove : function(e, t)
47861         {
47862             this.inKeyMode = false;
47863         },
47864         
47865         select : function(index, scrollIntoView)
47866         {
47867             this.selectedIndex = index;
47868             this.view.select(index);
47869             if(scrollIntoView !== false){
47870                 var el = this.view.getNode(index);
47871                 if(el){
47872                     this.list.scrollChildIntoView(el, false);
47873                 }
47874             }
47875         },
47876         
47877         createList : function()
47878         {
47879             this.list = Roo.get(document.body).createChild({
47880                 tag: 'ul',
47881                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
47882                 style: 'display:none'
47883             });
47884             
47885             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
47886         },
47887         
47888         collapseIf : function(e)
47889         {
47890             var in_combo  = e.within(this.el);
47891             var in_list =  e.within(this.list);
47892             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
47893             
47894             if (in_combo || in_list || is_list) {
47895                 return;
47896             }
47897             this.collapse();
47898         },
47899         
47900         onSelect : function(record, index)
47901         {
47902             if(this.fireEvent('beforeselect', this, record, index) !== false){
47903                 
47904                 this.setFlagClass(record.data.iso2);
47905                 this.setDialCode(record.data.dialCode);
47906                 this.hasFocus = false;
47907                 this.collapse();
47908                 this.fireEvent('select', this, record, index);
47909             }
47910         },
47911         
47912         flagEl : function()
47913         {
47914             var flag = this.el.select('div.flag',true).first();
47915             if(!flag){
47916                 return false;
47917             }
47918             return flag;
47919         },
47920         
47921         dialCodeHolderEl : function()
47922         {
47923             var d = this.el.select('input.dial-code-holder',true).first();
47924             if(!d){
47925                 return false;
47926             }
47927             return d;
47928         },
47929         
47930         setDialCode : function(v)
47931         {
47932             this.dialCodeHolder.dom.value = '+'+v;
47933         },
47934         
47935         setFlagClass : function(n)
47936         {
47937             this.flag.dom.className = 'flag '+n;
47938         },
47939         
47940         getValue : function()
47941         {
47942             var v = this.inputEl().getValue();
47943             if(this.dialCodeHolder) {
47944                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
47945             }
47946             return v;
47947         },
47948         
47949         setValue : function(v)
47950         {
47951             var d = this.getDialCode(v);
47952             
47953             //invalid dial code
47954             if(v.length == 0 || !d || d.length == 0) {
47955                 if(this.rendered){
47956                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
47957                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
47958                 }
47959                 return;
47960             }
47961             
47962             //valid dial code
47963             this.setFlagClass(this.dialCodeMapping[d].iso2);
47964             this.setDialCode(d);
47965             this.inputEl().dom.value = v.replace('+'+d,'');
47966             this.hiddenEl().dom.value = this.getValue();
47967             
47968             this.validate();
47969         },
47970         
47971         getDialCode : function(v)
47972         {
47973             v = v ||  '';
47974             
47975             if (v.length == 0) {
47976                 return this.dialCodeHolder.dom.value;
47977             }
47978             
47979             var dialCode = "";
47980             if (v.charAt(0) != "+") {
47981                 return false;
47982             }
47983             var numericChars = "";
47984             for (var i = 1; i < v.length; i++) {
47985               var c = v.charAt(i);
47986               if (!isNaN(c)) {
47987                 numericChars += c;
47988                 if (this.dialCodeMapping[numericChars]) {
47989                   dialCode = v.substr(1, i);
47990                 }
47991                 if (numericChars.length == 4) {
47992                   break;
47993                 }
47994               }
47995             }
47996             return dialCode;
47997         },
47998         
47999         reset : function()
48000         {
48001             this.setValue(this.defaultDialCode);
48002             this.validate();
48003         },
48004         
48005         hiddenEl : function()
48006         {
48007             return this.el.select('input.hidden-tel-input',true).first();
48008         },
48009         
48010         // after setting val
48011         onKeyUp : function(e){
48012             this.setValue(this.getValue());
48013         },
48014         
48015         onKeyPress : function(e){
48016             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48017                 e.stopEvent();
48018             }
48019         }
48020         
48021 });
48022 /**
48023  * @class Roo.bootstrap.form.MoneyField
48024  * @extends Roo.bootstrap.form.ComboBox
48025  * Bootstrap MoneyField class
48026  * 
48027  * @constructor
48028  * Create a new MoneyField.
48029  * @param {Object} config Configuration options
48030  */
48031
48032 Roo.bootstrap.form.MoneyField = function(config) {
48033     
48034     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48035     
48036 };
48037
48038 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48039     
48040     /**
48041      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48042      */
48043     allowDecimals : true,
48044     /**
48045      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48046      */
48047     decimalSeparator : ".",
48048     /**
48049      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48050      */
48051     decimalPrecision : 0,
48052     /**
48053      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48054      */
48055     allowNegative : true,
48056     /**
48057      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48058      */
48059     allowZero: true,
48060     /**
48061      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48062      */
48063     minValue : Number.NEGATIVE_INFINITY,
48064     /**
48065      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48066      */
48067     maxValue : Number.MAX_VALUE,
48068     /**
48069      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48070      */
48071     minText : "The minimum value for this field is {0}",
48072     /**
48073      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48074      */
48075     maxText : "The maximum value for this field is {0}",
48076     /**
48077      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
48078      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48079      */
48080     nanText : "{0} is not a valid number",
48081     /**
48082      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48083      */
48084     castInt : true,
48085     /**
48086      * @cfg {String} defaults currency of the MoneyField
48087      * value should be in lkey
48088      */
48089     defaultCurrency : false,
48090     /**
48091      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48092      */
48093     thousandsDelimiter : false,
48094     /**
48095      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48096      */
48097     max_length: false,
48098     
48099     inputlg : 9,
48100     inputmd : 9,
48101     inputsm : 9,
48102     inputxs : 6,
48103      /**
48104      * @cfg {Roo.data.Store} store  Store to lookup currency??
48105      */
48106     store : false,
48107     
48108     getAutoCreate : function()
48109     {
48110         var align = this.labelAlign || this.parentLabelAlign();
48111         
48112         var id = Roo.id();
48113
48114         var cfg = {
48115             cls: 'form-group',
48116             cn: []
48117         };
48118
48119         var input =  {
48120             tag: 'input',
48121             id : id,
48122             cls : 'form-control roo-money-amount-input',
48123             autocomplete: 'new-password'
48124         };
48125         
48126         var hiddenInput = {
48127             tag: 'input',
48128             type: 'hidden',
48129             id: Roo.id(),
48130             cls: 'hidden-number-input'
48131         };
48132         
48133         if(this.max_length) {
48134             input.maxlength = this.max_length; 
48135         }
48136         
48137         if (this.name) {
48138             hiddenInput.name = this.name;
48139         }
48140
48141         if (this.disabled) {
48142             input.disabled = true;
48143         }
48144
48145         var clg = 12 - this.inputlg;
48146         var cmd = 12 - this.inputmd;
48147         var csm = 12 - this.inputsm;
48148         var cxs = 12 - this.inputxs;
48149         
48150         var container = {
48151             tag : 'div',
48152             cls : 'row roo-money-field',
48153             cn : [
48154                 {
48155                     tag : 'div',
48156                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
48157                     cn : [
48158                         {
48159                             tag : 'div',
48160                             cls: 'roo-select2-container input-group',
48161                             cn: [
48162                                 {
48163                                     tag : 'input',
48164                                     cls : 'form-control roo-money-currency-input',
48165                                     autocomplete: 'new-password',
48166                                     readOnly : 1,
48167                                     name : this.currencyName
48168                                 },
48169                                 {
48170                                     tag :'span',
48171                                     cls : 'input-group-addon',
48172                                     cn : [
48173                                         {
48174                                             tag: 'span',
48175                                             cls: 'caret'
48176                                         }
48177                                     ]
48178                                 }
48179                             ]
48180                         }
48181                     ]
48182                 },
48183                 {
48184                     tag : 'div',
48185                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
48186                     cn : [
48187                         {
48188                             tag: 'div',
48189                             cls: this.hasFeedback ? 'has-feedback' : '',
48190                             cn: [
48191                                 input
48192                             ]
48193                         }
48194                     ]
48195                 }
48196             ]
48197             
48198         };
48199         
48200         if (this.fieldLabel.length) {
48201             var indicator = {
48202                 tag: 'i',
48203                 tooltip: 'This field is required'
48204             };
48205
48206             var label = {
48207                 tag: 'label',
48208                 'for':  id,
48209                 cls: 'control-label',
48210                 cn: []
48211             };
48212
48213             var label_text = {
48214                 tag: 'span',
48215                 html: this.fieldLabel
48216             };
48217
48218             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48219             label.cn = [
48220                 indicator,
48221                 label_text
48222             ];
48223
48224             if(this.indicatorpos == 'right') {
48225                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48226                 label.cn = [
48227                     label_text,
48228                     indicator
48229                 ];
48230             }
48231
48232             if(align == 'left') {
48233                 container = {
48234                     tag: 'div',
48235                     cn: [
48236                         container
48237                     ]
48238                 };
48239
48240                 if(this.labelWidth > 12){
48241                     label.style = "width: " + this.labelWidth + 'px';
48242                 }
48243                 if(this.labelWidth < 13 && this.labelmd == 0){
48244                     this.labelmd = this.labelWidth;
48245                 }
48246                 if(this.labellg > 0){
48247                     label.cls += ' col-lg-' + this.labellg;
48248                     input.cls += ' col-lg-' + (12 - this.labellg);
48249                 }
48250                 if(this.labelmd > 0){
48251                     label.cls += ' col-md-' + this.labelmd;
48252                     container.cls += ' col-md-' + (12 - this.labelmd);
48253                 }
48254                 if(this.labelsm > 0){
48255                     label.cls += ' col-sm-' + this.labelsm;
48256                     container.cls += ' col-sm-' + (12 - this.labelsm);
48257                 }
48258                 if(this.labelxs > 0){
48259                     label.cls += ' col-xs-' + this.labelxs;
48260                     container.cls += ' col-xs-' + (12 - this.labelxs);
48261                 }
48262             }
48263         }
48264
48265         cfg.cn = [
48266             label,
48267             container,
48268             hiddenInput
48269         ];
48270         
48271         var settings = this;
48272
48273         ['xs','sm','md','lg'].map(function(size){
48274             if (settings[size]) {
48275                 cfg.cls += ' col-' + size + '-' + settings[size];
48276             }
48277         });
48278         
48279         return cfg;
48280     },
48281     
48282     initEvents : function()
48283     {
48284         this.indicator = this.indicatorEl();
48285         
48286         this.initCurrencyEvent();
48287         
48288         this.initNumberEvent();
48289     },
48290     
48291     initCurrencyEvent : function()
48292     {
48293         if (!this.store) {
48294             throw "can not find store for combo";
48295         }
48296         
48297         this.store = Roo.factory(this.store, Roo.data);
48298         this.store.parent = this;
48299         
48300         this.createList();
48301         
48302         this.triggerEl = this.el.select('.input-group-addon', true).first();
48303         
48304         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
48305         
48306         var _this = this;
48307         
48308         (function(){
48309             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48310             _this.list.setWidth(lw);
48311         }).defer(100);
48312         
48313         this.list.on('mouseover', this.onViewOver, this);
48314         this.list.on('mousemove', this.onViewMove, this);
48315         this.list.on('scroll', this.onViewScroll, this);
48316         
48317         if(!this.tpl){
48318             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
48319         }
48320         
48321         this.view = new Roo.View(this.list, this.tpl, {
48322             singleSelect:true, store: this.store, selectedClass: this.selectedClass
48323         });
48324         
48325         this.view.on('click', this.onViewClick, this);
48326         
48327         this.store.on('beforeload', this.onBeforeLoad, this);
48328         this.store.on('load', this.onLoad, this);
48329         this.store.on('loadexception', this.onLoadException, this);
48330         
48331         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
48332             "up" : function(e){
48333                 this.inKeyMode = true;
48334                 this.selectPrev();
48335             },
48336
48337             "down" : function(e){
48338                 if(!this.isExpanded()){
48339                     this.onTriggerClick();
48340                 }else{
48341                     this.inKeyMode = true;
48342                     this.selectNext();
48343                 }
48344             },
48345
48346             "enter" : function(e){
48347                 this.collapse();
48348                 
48349                 if(this.fireEvent("specialkey", this, e)){
48350                     this.onViewClick(false);
48351                 }
48352                 
48353                 return true;
48354             },
48355
48356             "esc" : function(e){
48357                 this.collapse();
48358             },
48359
48360             "tab" : function(e){
48361                 this.collapse();
48362                 
48363                 if(this.fireEvent("specialkey", this, e)){
48364                     this.onViewClick(false);
48365                 }
48366                 
48367                 return true;
48368             },
48369
48370             scope : this,
48371
48372             doRelay : function(foo, bar, hname){
48373                 if(hname == 'down' || this.scope.isExpanded()){
48374                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48375                 }
48376                 return true;
48377             },
48378
48379             forceKeyDown: true
48380         });
48381         
48382         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
48383         
48384     },
48385     
48386     initNumberEvent : function(e)
48387     {
48388         this.inputEl().on("keydown" , this.fireKey,  this);
48389         this.inputEl().on("focus", this.onFocus,  this);
48390         this.inputEl().on("blur", this.onBlur,  this);
48391         
48392         this.inputEl().relayEvent('keyup', this);
48393         
48394         if(this.indicator){
48395             this.indicator.addClass('invisible');
48396         }
48397  
48398         this.originalValue = this.getValue();
48399         
48400         if(this.validationEvent == 'keyup'){
48401             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
48402             this.inputEl().on('keyup', this.filterValidation, this);
48403         }
48404         else if(this.validationEvent !== false){
48405             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
48406         }
48407         
48408         if(this.selectOnFocus){
48409             this.on("focus", this.preFocus, this);
48410             
48411         }
48412         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
48413             this.inputEl().on("keypress", this.filterKeys, this);
48414         } else {
48415             this.inputEl().relayEvent('keypress', this);
48416         }
48417         
48418         var allowed = "0123456789";
48419         
48420         if(this.allowDecimals){
48421             allowed += this.decimalSeparator;
48422         }
48423         
48424         if(this.allowNegative){
48425             allowed += "-";
48426         }
48427         
48428         if(this.thousandsDelimiter) {
48429             allowed += ",";
48430         }
48431         
48432         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
48433         
48434         var keyPress = function(e){
48435             
48436             var k = e.getKey();
48437             
48438             var c = e.getCharCode();
48439             
48440             if(
48441                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
48442                     allowed.indexOf(String.fromCharCode(c)) === -1
48443             ){
48444                 e.stopEvent();
48445                 return;
48446             }
48447             
48448             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
48449                 return;
48450             }
48451             
48452             if(allowed.indexOf(String.fromCharCode(c)) === -1){
48453                 e.stopEvent();
48454             }
48455         };
48456         
48457         this.inputEl().on("keypress", keyPress, this);
48458         
48459     },
48460     
48461     onTriggerClick : function(e)
48462     {   
48463         if(this.disabled){
48464             return;
48465         }
48466         
48467         this.page = 0;
48468         this.loadNext = false;
48469         
48470         if(this.isExpanded()){
48471             this.collapse();
48472             return;
48473         }
48474         
48475         this.hasFocus = true;
48476         
48477         if(this.triggerAction == 'all') {
48478             this.doQuery(this.allQuery, true);
48479             return;
48480         }
48481         
48482         this.doQuery(this.getRawValue());
48483     },
48484     
48485     getCurrency : function()
48486     {   
48487         var v = this.currencyEl().getValue();
48488         
48489         return v;
48490     },
48491     
48492     restrictHeight : function()
48493     {
48494         this.list.alignTo(this.currencyEl(), this.listAlign);
48495         this.list.alignTo(this.currencyEl(), this.listAlign);
48496     },
48497     
48498     onViewClick : function(view, doFocus, el, e)
48499     {
48500         var index = this.view.getSelectedIndexes()[0];
48501         
48502         var r = this.store.getAt(index);
48503         
48504         if(r){
48505             this.onSelect(r, index);
48506         }
48507     },
48508     
48509     onSelect : function(record, index){
48510         
48511         if(this.fireEvent('beforeselect', this, record, index) !== false){
48512         
48513             this.setFromCurrencyData(index > -1 ? record.data : false);
48514             
48515             this.collapse();
48516             
48517             this.fireEvent('select', this, record, index);
48518         }
48519     },
48520     
48521     setFromCurrencyData : function(o)
48522     {
48523         var currency = '';
48524         
48525         this.lastCurrency = o;
48526         
48527         if (this.currencyField) {
48528             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
48529         } else {
48530             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
48531         }
48532         
48533         this.lastSelectionText = currency;
48534         
48535         //setting default currency
48536         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
48537             this.setCurrency(this.defaultCurrency);
48538             return;
48539         }
48540         
48541         this.setCurrency(currency);
48542     },
48543     
48544     setFromData : function(o)
48545     {
48546         var c = {};
48547         
48548         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
48549         
48550         this.setFromCurrencyData(c);
48551         
48552         var value = '';
48553         
48554         if (this.name) {
48555             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
48556         } else {
48557             Roo.log('no value set for '+ (this.name ? this.name : this.id));
48558         }
48559         
48560         this.setValue(value);
48561         
48562     },
48563     
48564     setCurrency : function(v)
48565     {   
48566         this.currencyValue = v;
48567         
48568         if(this.rendered){
48569             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
48570             this.validate();
48571         }
48572     },
48573     
48574     setValue : function(v)
48575     {
48576         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
48577         
48578         this.value = v;
48579         
48580         if(this.rendered){
48581             
48582             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48583             
48584             this.inputEl().dom.value = (v == '') ? '' :
48585                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
48586             
48587             if(!this.allowZero && v === '0') {
48588                 this.hiddenEl().dom.value = '';
48589                 this.inputEl().dom.value = '';
48590             }
48591             
48592             this.validate();
48593         }
48594     },
48595     
48596     getRawValue : function()
48597     {
48598         var v = this.inputEl().getValue();
48599         
48600         return v;
48601     },
48602     
48603     getValue : function()
48604     {
48605         return this.fixPrecision(this.parseValue(this.getRawValue()));
48606     },
48607     
48608     parseValue : function(value)
48609     {
48610         if(this.thousandsDelimiter) {
48611             value += "";
48612             r = new RegExp(",", "g");
48613             value = value.replace(r, "");
48614         }
48615         
48616         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
48617         return isNaN(value) ? '' : value;
48618         
48619     },
48620     
48621     fixPrecision : function(value)
48622     {
48623         if(this.thousandsDelimiter) {
48624             value += "";
48625             r = new RegExp(",", "g");
48626             value = value.replace(r, "");
48627         }
48628         
48629         var nan = isNaN(value);
48630         
48631         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
48632             return nan ? '' : value;
48633         }
48634         return parseFloat(value).toFixed(this.decimalPrecision);
48635     },
48636     
48637     decimalPrecisionFcn : function(v)
48638     {
48639         return Math.floor(v);
48640     },
48641     
48642     validateValue : function(value)
48643     {
48644         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
48645             return false;
48646         }
48647         
48648         var num = this.parseValue(value);
48649         
48650         if(isNaN(num)){
48651             this.markInvalid(String.format(this.nanText, value));
48652             return false;
48653         }
48654         
48655         if(num < this.minValue){
48656             this.markInvalid(String.format(this.minText, this.minValue));
48657             return false;
48658         }
48659         
48660         if(num > this.maxValue){
48661             this.markInvalid(String.format(this.maxText, this.maxValue));
48662             return false;
48663         }
48664         
48665         return true;
48666     },
48667     
48668     validate : function()
48669     {
48670         if(this.disabled || this.allowBlank){
48671             this.markValid();
48672             return true;
48673         }
48674         
48675         var currency = this.getCurrency();
48676         
48677         if(this.validateValue(this.getRawValue()) && currency.length){
48678             this.markValid();
48679             return true;
48680         }
48681         
48682         this.markInvalid();
48683         return false;
48684     },
48685     
48686     getName: function()
48687     {
48688         return this.name;
48689     },
48690     
48691     beforeBlur : function()
48692     {
48693         if(!this.castInt){
48694             return;
48695         }
48696         
48697         var v = this.parseValue(this.getRawValue());
48698         
48699         if(v || v == 0){
48700             this.setValue(v);
48701         }
48702     },
48703     
48704     onBlur : function()
48705     {
48706         this.beforeBlur();
48707         
48708         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
48709             //this.el.removeClass(this.focusClass);
48710         }
48711         
48712         this.hasFocus = false;
48713         
48714         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
48715             this.validate();
48716         }
48717         
48718         var v = this.getValue();
48719         
48720         if(String(v) !== String(this.startValue)){
48721             this.fireEvent('change', this, v, this.startValue);
48722         }
48723         
48724         this.fireEvent("blur", this);
48725     },
48726     
48727     inputEl : function()
48728     {
48729         return this.el.select('.roo-money-amount-input', true).first();
48730     },
48731     
48732     currencyEl : function()
48733     {
48734         return this.el.select('.roo-money-currency-input', true).first();
48735     },
48736     
48737     hiddenEl : function()
48738     {
48739         return this.el.select('input.hidden-number-input',true).first();
48740     }
48741     
48742 });/**
48743  * @class Roo.bootstrap.BezierSignature
48744  * @extends Roo.bootstrap.Component
48745  * Bootstrap BezierSignature class
48746  * This script refer to:
48747  *    Title: Signature Pad
48748  *    Author: szimek
48749  *    Availability: https://github.com/szimek/signature_pad
48750  *
48751  * @constructor
48752  * Create a new BezierSignature
48753  * @param {Object} config The config object
48754  */
48755
48756 Roo.bootstrap.BezierSignature = function(config){
48757     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
48758     this.addEvents({
48759         "resize" : true
48760     });
48761 };
48762
48763 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
48764 {
48765      
48766     curve_data: [],
48767     
48768     is_empty: true,
48769     
48770     mouse_btn_down: true,
48771     
48772     /**
48773      * @cfg {int} canvas height
48774      */
48775     canvas_height: '200px',
48776     
48777     /**
48778      * @cfg {float|function} Radius of a single dot.
48779      */ 
48780     dot_size: false,
48781     
48782     /**
48783      * @cfg {float} Minimum width of a line. Defaults to 0.5.
48784      */
48785     min_width: 0.5,
48786     
48787     /**
48788      * @cfg {float} Maximum width of a line. Defaults to 2.5.
48789      */
48790     max_width: 2.5,
48791     
48792     /**
48793      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
48794      */
48795     throttle: 16,
48796     
48797     /**
48798      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
48799      */
48800     min_distance: 5,
48801     
48802     /**
48803      * @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.
48804      */
48805     bg_color: 'rgba(0, 0, 0, 0)',
48806     
48807     /**
48808      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
48809      */
48810     dot_color: 'black',
48811     
48812     /**
48813      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
48814      */ 
48815     velocity_filter_weight: 0.7,
48816     
48817     /**
48818      * @cfg {function} Callback when stroke begin. 
48819      */
48820     onBegin: false,
48821     
48822     /**
48823      * @cfg {function} Callback when stroke end.
48824      */
48825     onEnd: false,
48826     
48827     getAutoCreate : function()
48828     {
48829         var cls = 'roo-signature column';
48830         
48831         if(this.cls){
48832             cls += ' ' + this.cls;
48833         }
48834         
48835         var col_sizes = [
48836             'lg',
48837             'md',
48838             'sm',
48839             'xs'
48840         ];
48841         
48842         for(var i = 0; i < col_sizes.length; i++) {
48843             if(this[col_sizes[i]]) {
48844                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
48845             }
48846         }
48847         
48848         var cfg = {
48849             tag: 'div',
48850             cls: cls,
48851             cn: [
48852                 {
48853                     tag: 'div',
48854                     cls: 'roo-signature-body',
48855                     cn: [
48856                         {
48857                             tag: 'canvas',
48858                             cls: 'roo-signature-body-canvas',
48859                             height: this.canvas_height,
48860                             width: this.canvas_width
48861                         }
48862                     ]
48863                 },
48864                 {
48865                     tag: 'input',
48866                     type: 'file',
48867                     style: 'display: none'
48868                 }
48869             ]
48870         };
48871         
48872         return cfg;
48873     },
48874     
48875     initEvents: function() 
48876     {
48877         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
48878         
48879         var canvas = this.canvasEl();
48880         
48881         // mouse && touch event swapping...
48882         canvas.dom.style.touchAction = 'none';
48883         canvas.dom.style.msTouchAction = 'none';
48884         
48885         this.mouse_btn_down = false;
48886         canvas.on('mousedown', this._handleMouseDown, this);
48887         canvas.on('mousemove', this._handleMouseMove, this);
48888         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
48889         
48890         if (window.PointerEvent) {
48891             canvas.on('pointerdown', this._handleMouseDown, this);
48892             canvas.on('pointermove', this._handleMouseMove, this);
48893             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
48894         }
48895         
48896         if ('ontouchstart' in window) {
48897             canvas.on('touchstart', this._handleTouchStart, this);
48898             canvas.on('touchmove', this._handleTouchMove, this);
48899             canvas.on('touchend', this._handleTouchEnd, this);
48900         }
48901         
48902         Roo.EventManager.onWindowResize(this.resize, this, true);
48903         
48904         // file input event
48905         this.fileEl().on('change', this.uploadImage, this);
48906         
48907         this.clear();
48908         
48909         this.resize();
48910     },
48911     
48912     resize: function(){
48913         
48914         var canvas = this.canvasEl().dom;
48915         var ctx = this.canvasElCtx();
48916         var img_data = false;
48917         
48918         if(canvas.width > 0) {
48919             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
48920         }
48921         // setting canvas width will clean img data
48922         canvas.width = 0;
48923         
48924         var style = window.getComputedStyle ? 
48925             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
48926             
48927         var padding_left = parseInt(style.paddingLeft) || 0;
48928         var padding_right = parseInt(style.paddingRight) || 0;
48929         
48930         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
48931         
48932         if(img_data) {
48933             ctx.putImageData(img_data, 0, 0);
48934         }
48935     },
48936     
48937     _handleMouseDown: function(e)
48938     {
48939         if (e.browserEvent.which === 1) {
48940             this.mouse_btn_down = true;
48941             this.strokeBegin(e);
48942         }
48943     },
48944     
48945     _handleMouseMove: function (e)
48946     {
48947         if (this.mouse_btn_down) {
48948             this.strokeMoveUpdate(e);
48949         }
48950     },
48951     
48952     _handleMouseUp: function (e)
48953     {
48954         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
48955             this.mouse_btn_down = false;
48956             this.strokeEnd(e);
48957         }
48958     },
48959     
48960     _handleTouchStart: function (e) {
48961         
48962         e.preventDefault();
48963         if (e.browserEvent.targetTouches.length === 1) {
48964             // var touch = e.browserEvent.changedTouches[0];
48965             // this.strokeBegin(touch);
48966             
48967              this.strokeBegin(e); // assume e catching the correct xy...
48968         }
48969     },
48970     
48971     _handleTouchMove: function (e) {
48972         e.preventDefault();
48973         // var touch = event.targetTouches[0];
48974         // _this._strokeMoveUpdate(touch);
48975         this.strokeMoveUpdate(e);
48976     },
48977     
48978     _handleTouchEnd: function (e) {
48979         var wasCanvasTouched = e.target === this.canvasEl().dom;
48980         if (wasCanvasTouched) {
48981             e.preventDefault();
48982             // var touch = event.changedTouches[0];
48983             // _this._strokeEnd(touch);
48984             this.strokeEnd(e);
48985         }
48986     },
48987     
48988     reset: function () {
48989         this._lastPoints = [];
48990         this._lastVelocity = 0;
48991         this._lastWidth = (this.min_width + this.max_width) / 2;
48992         this.canvasElCtx().fillStyle = this.dot_color;
48993     },
48994     
48995     strokeMoveUpdate: function(e)
48996     {
48997         this.strokeUpdate(e);
48998         
48999         if (this.throttle) {
49000             this.throttleStroke(this.strokeUpdate, this.throttle);
49001         }
49002         else {
49003             this.strokeUpdate(e);
49004         }
49005     },
49006     
49007     strokeBegin: function(e)
49008     {
49009         var newPointGroup = {
49010             color: this.dot_color,
49011             points: []
49012         };
49013         
49014         if (typeof this.onBegin === 'function') {
49015             this.onBegin(e);
49016         }
49017         
49018         this.curve_data.push(newPointGroup);
49019         this.reset();
49020         this.strokeUpdate(e);
49021     },
49022     
49023     strokeUpdate: function(e)
49024     {
49025         var rect = this.canvasEl().dom.getBoundingClientRect();
49026         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49027         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49028         var lastPoints = lastPointGroup.points;
49029         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49030         var isLastPointTooClose = lastPoint
49031             ? point.distanceTo(lastPoint) <= this.min_distance
49032             : false;
49033         var color = lastPointGroup.color;
49034         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49035             var curve = this.addPoint(point);
49036             if (!lastPoint) {
49037                 this.drawDot({color: color, point: point});
49038             }
49039             else if (curve) {
49040                 this.drawCurve({color: color, curve: curve});
49041             }
49042             lastPoints.push({
49043                 time: point.time,
49044                 x: point.x,
49045                 y: point.y
49046             });
49047         }
49048     },
49049     
49050     strokeEnd: function(e)
49051     {
49052         this.strokeUpdate(e);
49053         if (typeof this.onEnd === 'function') {
49054             this.onEnd(e);
49055         }
49056     },
49057     
49058     addPoint:  function (point) {
49059         var _lastPoints = this._lastPoints;
49060         _lastPoints.push(point);
49061         if (_lastPoints.length > 2) {
49062             if (_lastPoints.length === 3) {
49063                 _lastPoints.unshift(_lastPoints[0]);
49064             }
49065             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49066             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49067             _lastPoints.shift();
49068             return curve;
49069         }
49070         return null;
49071     },
49072     
49073     calculateCurveWidths: function (startPoint, endPoint) {
49074         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49075             (1 - this.velocity_filter_weight) * this._lastVelocity;
49076
49077         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49078         var widths = {
49079             end: newWidth,
49080             start: this._lastWidth
49081         };
49082         
49083         this._lastVelocity = velocity;
49084         this._lastWidth = newWidth;
49085         return widths;
49086     },
49087     
49088     drawDot: function (_a) {
49089         var color = _a.color, point = _a.point;
49090         var ctx = this.canvasElCtx();
49091         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49092         ctx.beginPath();
49093         this.drawCurveSegment(point.x, point.y, width);
49094         ctx.closePath();
49095         ctx.fillStyle = color;
49096         ctx.fill();
49097     },
49098     
49099     drawCurve: function (_a) {
49100         var color = _a.color, curve = _a.curve;
49101         var ctx = this.canvasElCtx();
49102         var widthDelta = curve.endWidth - curve.startWidth;
49103         var drawSteps = Math.floor(curve.length()) * 2;
49104         ctx.beginPath();
49105         ctx.fillStyle = color;
49106         for (var i = 0; i < drawSteps; i += 1) {
49107         var t = i / drawSteps;
49108         var tt = t * t;
49109         var ttt = tt * t;
49110         var u = 1 - t;
49111         var uu = u * u;
49112         var uuu = uu * u;
49113         var x = uuu * curve.startPoint.x;
49114         x += 3 * uu * t * curve.control1.x;
49115         x += 3 * u * tt * curve.control2.x;
49116         x += ttt * curve.endPoint.x;
49117         var y = uuu * curve.startPoint.y;
49118         y += 3 * uu * t * curve.control1.y;
49119         y += 3 * u * tt * curve.control2.y;
49120         y += ttt * curve.endPoint.y;
49121         var width = curve.startWidth + ttt * widthDelta;
49122         this.drawCurveSegment(x, y, width);
49123         }
49124         ctx.closePath();
49125         ctx.fill();
49126     },
49127     
49128     drawCurveSegment: function (x, y, width) {
49129         var ctx = this.canvasElCtx();
49130         ctx.moveTo(x, y);
49131         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
49132         this.is_empty = false;
49133     },
49134     
49135     clear: function()
49136     {
49137         var ctx = this.canvasElCtx();
49138         var canvas = this.canvasEl().dom;
49139         ctx.fillStyle = this.bg_color;
49140         ctx.clearRect(0, 0, canvas.width, canvas.height);
49141         ctx.fillRect(0, 0, canvas.width, canvas.height);
49142         this.curve_data = [];
49143         this.reset();
49144         this.is_empty = true;
49145     },
49146     
49147     fileEl: function()
49148     {
49149         return  this.el.select('input',true).first();
49150     },
49151     
49152     canvasEl: function()
49153     {
49154         return this.el.select('canvas',true).first();
49155     },
49156     
49157     canvasElCtx: function()
49158     {
49159         return this.el.select('canvas',true).first().dom.getContext('2d');
49160     },
49161     
49162     getImage: function(type)
49163     {
49164         if(this.is_empty) {
49165             return false;
49166         }
49167         
49168         // encryption ?
49169         return this.canvasEl().dom.toDataURL('image/'+type, 1);
49170     },
49171     
49172     drawFromImage: function(img_src)
49173     {
49174         var img = new Image();
49175         
49176         img.onload = function(){
49177             this.canvasElCtx().drawImage(img, 0, 0);
49178         }.bind(this);
49179         
49180         img.src = img_src;
49181         
49182         this.is_empty = false;
49183     },
49184     
49185     selectImage: function()
49186     {
49187         this.fileEl().dom.click();
49188     },
49189     
49190     uploadImage: function(e)
49191     {
49192         var reader = new FileReader();
49193         
49194         reader.onload = function(e){
49195             var img = new Image();
49196             img.onload = function(){
49197                 this.reset();
49198                 this.canvasElCtx().drawImage(img, 0, 0);
49199             }.bind(this);
49200             img.src = e.target.result;
49201         }.bind(this);
49202         
49203         reader.readAsDataURL(e.target.files[0]);
49204     },
49205     
49206     // Bezier Point Constructor
49207     Point: (function () {
49208         function Point(x, y, time) {
49209             this.x = x;
49210             this.y = y;
49211             this.time = time || Date.now();
49212         }
49213         Point.prototype.distanceTo = function (start) {
49214             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
49215         };
49216         Point.prototype.equals = function (other) {
49217             return this.x === other.x && this.y === other.y && this.time === other.time;
49218         };
49219         Point.prototype.velocityFrom = function (start) {
49220             return this.time !== start.time
49221             ? this.distanceTo(start) / (this.time - start.time)
49222             : 0;
49223         };
49224         return Point;
49225     }()),
49226     
49227     
49228     // Bezier Constructor
49229     Bezier: (function () {
49230         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
49231             this.startPoint = startPoint;
49232             this.control2 = control2;
49233             this.control1 = control1;
49234             this.endPoint = endPoint;
49235             this.startWidth = startWidth;
49236             this.endWidth = endWidth;
49237         }
49238         Bezier.fromPoints = function (points, widths, scope) {
49239             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
49240             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
49241             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
49242         };
49243         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
49244             var dx1 = s1.x - s2.x;
49245             var dy1 = s1.y - s2.y;
49246             var dx2 = s2.x - s3.x;
49247             var dy2 = s2.y - s3.y;
49248             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
49249             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
49250             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
49251             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
49252             var dxm = m1.x - m2.x;
49253             var dym = m1.y - m2.y;
49254             var k = l2 / (l1 + l2);
49255             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
49256             var tx = s2.x - cm.x;
49257             var ty = s2.y - cm.y;
49258             return {
49259                 c1: new scope.Point(m1.x + tx, m1.y + ty),
49260                 c2: new scope.Point(m2.x + tx, m2.y + ty)
49261             };
49262         };
49263         Bezier.prototype.length = function () {
49264             var steps = 10;
49265             var length = 0;
49266             var px;
49267             var py;
49268             for (var i = 0; i <= steps; i += 1) {
49269                 var t = i / steps;
49270                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
49271                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
49272                 if (i > 0) {
49273                     var xdiff = cx - px;
49274                     var ydiff = cy - py;
49275                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
49276                 }
49277                 px = cx;
49278                 py = cy;
49279             }
49280             return length;
49281         };
49282         Bezier.prototype.point = function (t, start, c1, c2, end) {
49283             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
49284             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
49285             + (3.0 * c2 * (1.0 - t) * t * t)
49286             + (end * t * t * t);
49287         };
49288         return Bezier;
49289     }()),
49290     
49291     throttleStroke: function(fn, wait) {
49292       if (wait === void 0) { wait = 250; }
49293       var previous = 0;
49294       var timeout = null;
49295       var result;
49296       var storedContext;
49297       var storedArgs;
49298       var later = function () {
49299           previous = Date.now();
49300           timeout = null;
49301           result = fn.apply(storedContext, storedArgs);
49302           if (!timeout) {
49303               storedContext = null;
49304               storedArgs = [];
49305           }
49306       };
49307       return function wrapper() {
49308           var args = [];
49309           for (var _i = 0; _i < arguments.length; _i++) {
49310               args[_i] = arguments[_i];
49311           }
49312           var now = Date.now();
49313           var remaining = wait - (now - previous);
49314           storedContext = this;
49315           storedArgs = args;
49316           if (remaining <= 0 || remaining > wait) {
49317               if (timeout) {
49318                   clearTimeout(timeout);
49319                   timeout = null;
49320               }
49321               previous = now;
49322               result = fn.apply(storedContext, storedArgs);
49323               if (!timeout) {
49324                   storedContext = null;
49325                   storedArgs = [];
49326               }
49327           }
49328           else if (!timeout) {
49329               timeout = window.setTimeout(later, remaining);
49330           }
49331           return result;
49332       };
49333   }
49334   
49335 });
49336
49337  
49338
49339  // old names for form elements
49340 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
49341 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
49342 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
49343 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
49344 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
49345 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
49346 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
49347 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
49348 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
49349 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
49350 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
49351 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
49352 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
49353 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
49354 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
49355 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
49356 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
49357 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
49358 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
49359 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
49360 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
49361 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
49362 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
49363 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
49364 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
49365 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
49366
49367 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
49368 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
49369
49370 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
49371 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
49372
49373 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
49374 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
49375 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
49376 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
49377