roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 Roo.bootstrap = {};/**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
19
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};/*
21  * Based on:
22  * Ext JS Library 1.1.1
23  * Copyright(c) 2006-2007, Ext JS, LLC.
24  *
25  * Originally Released Under LGPL - original licence link has changed is not relivant.
26  *
27  * Fork - LGPL
28  * <script type="text/javascript">
29  */
30
31
32 /**
33  * @class Roo.Shadow
34  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
35  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
36  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
37  * @constructor
38  * Create a new Shadow
39  * @param {Object} config The config object
40  */
41 Roo.Shadow = function(config){
42     Roo.apply(this, config);
43     if(typeof this.mode != "string"){
44         this.mode = this.defaultMode;
45     }
46     var o = this.offset, a = {h: 0};
47     var rad = Math.floor(this.offset/2);
48     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
49         case "drop":
50             a.w = 0;
51             a.l = a.t = o;
52             a.t -= 1;
53             if(Roo.isIE){
54                 a.l -= this.offset + rad;
55                 a.t -= this.offset + rad;
56                 a.w -= rad;
57                 a.h -= rad;
58                 a.t += 1;
59             }
60         break;
61         case "sides":
62             a.w = (o*2);
63             a.l = -o;
64             a.t = o-1;
65             if(Roo.isIE){
66                 a.l -= (this.offset - rad);
67                 a.t -= this.offset + rad;
68                 a.l += 1;
69                 a.w -= (this.offset - rad)*2;
70                 a.w -= rad + 1;
71                 a.h -= 1;
72             }
73         break;
74         case "frame":
75             a.w = a.h = (o*2);
76             a.l = a.t = -o;
77             a.t += 1;
78             a.h -= 2;
79             if(Roo.isIE){
80                 a.l -= (this.offset - rad);
81                 a.t -= (this.offset - rad);
82                 a.l += 1;
83                 a.w -= (this.offset + rad + 1);
84                 a.h -= (this.offset + rad);
85                 a.h += 1;
86             }
87         break;
88     };
89
90     this.adjusts = a;
91 };
92
93 Roo.Shadow.prototype = {
94     /**
95      * @cfg {String} mode
96      * The shadow display mode.  Supports the following options:<br />
97      * sides: Shadow displays on both sides and bottom only<br />
98      * frame: Shadow displays equally on all four sides<br />
99      * drop: Traditional bottom-right drop shadow (default)
100      */
101     mode: false,
102     /**
103      * @cfg {String} offset
104      * The number of pixels to offset the shadow from the element (defaults to 4)
105      */
106     offset: 4,
107
108     // private
109     defaultMode: "drop",
110
111     /**
112      * Displays the shadow under the target element
113      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
114      */
115     show : function(target){
116         target = Roo.get(target);
117         if(!this.el){
118             this.el = Roo.Shadow.Pool.pull();
119             if(this.el.dom.nextSibling != target.dom){
120                 this.el.insertBefore(target);
121             }
122         }
123         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
124         if(Roo.isIE){
125             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
126         }
127         this.realign(
128             target.getLeft(true),
129             target.getTop(true),
130             target.getWidth(),
131             target.getHeight()
132         );
133         this.el.dom.style.display = "block";
134     },
135
136     /**
137      * Returns true if the shadow is visible, else false
138      */
139     isVisible : function(){
140         return this.el ? true : false;  
141     },
142
143     /**
144      * Direct alignment when values are already available. Show must be called at least once before
145      * calling this method to ensure it is initialized.
146      * @param {Number} left The target element left position
147      * @param {Number} top The target element top position
148      * @param {Number} width The target element width
149      * @param {Number} height The target element height
150      */
151     realign : function(l, t, w, h){
152         if(!this.el){
153             return;
154         }
155         var a = this.adjusts, d = this.el.dom, s = d.style;
156         var iea = 0;
157         s.left = (l+a.l)+"px";
158         s.top = (t+a.t)+"px";
159         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
160  
161         if(s.width != sws || s.height != shs){
162             s.width = sws;
163             s.height = shs;
164             if(!Roo.isIE){
165                 var cn = d.childNodes;
166                 var sww = Math.max(0, (sw-12))+"px";
167                 cn[0].childNodes[1].style.width = sww;
168                 cn[1].childNodes[1].style.width = sww;
169                 cn[2].childNodes[1].style.width = sww;
170                 cn[1].style.height = Math.max(0, (sh-12))+"px";
171             }
172         }
173     },
174
175     /**
176      * Hides this shadow
177      */
178     hide : function(){
179         if(this.el){
180             this.el.dom.style.display = "none";
181             Roo.Shadow.Pool.push(this.el);
182             delete this.el;
183         }
184     },
185
186     /**
187      * Adjust the z-index of this shadow
188      * @param {Number} zindex The new z-index
189      */
190     setZIndex : function(z){
191         this.zIndex = z;
192         if(this.el){
193             this.el.setStyle("z-index", z);
194         }
195     }
196 };
197
198 // Private utility class that manages the internal Shadow cache
199 Roo.Shadow.Pool = function(){
200     var p = [];
201     var markup = Roo.isIE ?
202                  '<div class="x-ie-shadow"></div>' :
203                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204     return {
205         pull : function(){
206             var sh = p.shift();
207             if(!sh){
208                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
209                 sh.autoBoxAdjust = false;
210             }
211             return sh;
212         },
213
214         push : function(sh){
215             p.push(sh);
216         }
217     };
218 }();/*
219  * - LGPL
220  *
221  * base class for bootstrap elements.
222  * 
223  */
224
225 Roo.bootstrap = Roo.bootstrap || {};
226 /**
227  * @class Roo.bootstrap.Component
228  * @extends Roo.Component
229  * @abstract
230  * @children Roo.bootstrap.Component
231  * Bootstrap Component base class
232  * @cfg {String} cls css class
233  * @cfg {String} style any extra css
234  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
235  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
236  * @cfg {string} dataId cutomer id
237  * @cfg {string} name Specifies name attribute
238  * @cfg {string} tooltip  Text for the tooltip
239  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
240  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
241  
242  * @constructor
243  * Do not use directly - it does not do anything..
244  * @param {Object} config The config object
245  */
246
247
248
249 Roo.bootstrap.Component = function(config){
250     Roo.bootstrap.Component.superclass.constructor.call(this, config);
251        
252     this.addEvents({
253         /**
254          * @event childrenrendered
255          * Fires when the children have been rendered..
256          * @param {Roo.bootstrap.Component} this
257          */
258         "childrenrendered" : true
259         
260         
261         
262     });
263     
264     
265 };
266
267 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
268     
269     
270     allowDomMove : false, // to stop relocations in parent onRender...
271     
272     cls : false,
273     
274     style : false,
275     
276     autoCreate : false,
277     
278     tooltip : null,
279     /**
280      * Initialize Events for the element
281      */
282     initEvents : function() { },
283     
284     xattr : false,
285     
286     parentId : false,
287     
288     can_build_overlaid : true,
289     
290     container_method : false,
291     
292     dataId : false,
293     
294     name : false,
295     
296     parent: function() {
297         // returns the parent component..
298         return Roo.ComponentMgr.get(this.parentId)
299         
300         
301     },
302     
303     // private
304     onRender : function(ct, position)
305     {
306        // Roo.log("Call onRender: " + this.xtype);
307         
308         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
309         
310         if(this.el){
311             if (this.el.attr('xtype')) {
312                 this.el.attr('xtypex', this.el.attr('xtype'));
313                 this.el.dom.removeAttribute('xtype');
314                 
315                 this.initEvents();
316             }
317             
318             return;
319         }
320         
321          
322         
323         var cfg = Roo.apply({},  this.getAutoCreate());
324         
325         cfg.id = this.id || Roo.id();
326         
327         // fill in the extra attributes 
328         if (this.xattr && typeof(this.xattr) =='object') {
329             for (var i in this.xattr) {
330                 cfg[i] = this.xattr[i];
331             }
332         }
333         
334         if(this.dataId){
335             cfg.dataId = this.dataId;
336         }
337         
338         if (this.cls) {
339             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
340         }
341         
342         if (this.style) { // fixme needs to support more complex style data.
343             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
344         }
345         
346         if(this.name){
347             cfg.name = this.name;
348         }
349         
350         this.el = ct.createChild(cfg, position);
351         
352         if (this.tooltip) {
353             this.tooltipEl().attr('tooltip', this.tooltip);
354         }
355         
356         if(this.tabIndex !== undefined){
357             this.el.dom.setAttribute('tabIndex', this.tabIndex);
358         }
359         
360         this.initEvents();
361         
362     },
363     /**
364      * Fetch the element to add children to
365      * @return {Roo.Element} defaults to this.el
366      */
367     getChildContainer : function()
368     {
369         return this.el;
370     },
371     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
372     {
373         return Roo.get(document.body);
374     },
375     
376     /**
377      * Fetch the element to display the tooltip on.
378      * @return {Roo.Element} defaults to this.el
379      */
380     tooltipEl : function()
381     {
382         return this.el;
383     },
384         
385     addxtype  : function(tree,cntr)
386     {
387         var cn = this;
388         
389         cn = Roo.factory(tree);
390         //Roo.log(['addxtype', cn]);
391            
392         cn.parentType = this.xtype; //??
393         cn.parentId = this.id;
394         
395         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
396         if (typeof(cn.container_method) == 'string') {
397             cntr = cn.container_method;
398         }
399         
400         
401         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
402         
403         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
404         
405         var build_from_html =  Roo.XComponent.build_from_html;
406           
407         var is_body  = (tree.xtype == 'Body') ;
408           
409         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
410           
411         var self_cntr_el = Roo.get(this[cntr](false));
412         
413         // do not try and build conditional elements 
414         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
415             return false;
416         }
417         
418         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
419             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
420                 return this.addxtypeChild(tree,cntr, is_body);
421             }
422             
423             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
424                 
425             if(echild){
426                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
427             }
428             
429             Roo.log('skipping render');
430             return cn;
431             
432         }
433         
434         var ret = false;
435         if (!build_from_html) {
436             return false;
437         }
438         
439         // this i think handles overlaying multiple children of the same type
440         // with the sam eelement.. - which might be buggy..
441         while (true) {
442             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
443             
444             if (!echild) {
445                 break;
446             }
447             
448             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
449                 break;
450             }
451             
452             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453         }
454        
455         return ret;
456     },
457     
458     
459     addxtypeChild : function (tree, cntr, is_body)
460     {
461         Roo.debug && Roo.log('addxtypeChild:' + cntr);
462         var cn = this;
463         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
464         
465         
466         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
467                     (typeof(tree['flexy:foreach']) != 'undefined');
468           
469     
470         
471         skip_children = false;
472         // render the element if it's not BODY.
473         if (!is_body) {
474             
475             // if parent was disabled, then do not try and create the children..
476             if(!this[cntr](true)){
477                 tree.items = [];
478                 return tree;
479             }
480            
481             cn = Roo.factory(tree);
482            
483             cn.parentType = this.xtype; //??
484             cn.parentId = this.id;
485             
486             var build_from_html =  Roo.XComponent.build_from_html;
487             
488             
489             // does the container contain child eleemnts with 'xtype' attributes.
490             // that match this xtype..
491             // note - when we render we create these as well..
492             // so we should check to see if body has xtype set.
493             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
494                
495                 var self_cntr_el = Roo.get(this[cntr](false));
496                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
497                 if (echild) { 
498                     //Roo.log(Roo.XComponent.build_from_html);
499                     //Roo.log("got echild:");
500                     //Roo.log(echild);
501                 }
502                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
503                 // and are not displayed -this causes this to use up the wrong element when matching.
504                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
505                 
506                 
507                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
508                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
509                   
510                   
511                   
512                     cn.el = echild;
513                   //  Roo.log("GOT");
514                     //echild.dom.removeAttribute('xtype');
515                 } else {
516                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
517                     Roo.debug && Roo.log(self_cntr_el);
518                     Roo.debug && Roo.log(echild);
519                     Roo.debug && Roo.log(cn);
520                 }
521             }
522            
523             
524            
525             // if object has flexy:if - then it may or may not be rendered.
526             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
527                 // skip a flexy if element.
528                 Roo.debug && Roo.log('skipping render');
529                 Roo.debug && Roo.log(tree);
530                 if (!cn.el) {
531                     Roo.debug && Roo.log('skipping all children');
532                     skip_children = true;
533                 }
534                 
535              } else {
536                  
537                 // actually if flexy:foreach is found, we really want to create 
538                 // multiple copies here...
539                 //Roo.log('render');
540                 //Roo.log(this[cntr]());
541                 // some elements do not have render methods.. like the layouts...
542                 /*
543                 if(this[cntr](true) === false){
544                     cn.items = [];
545                     return cn;
546                 }
547                 */
548                 cn.render && cn.render(this[cntr](true));
549                 
550              }
551             // then add the element..
552         }
553          
554         // handle the kids..
555         
556         var nitems = [];
557         /*
558         if (typeof (tree.menu) != 'undefined') {
559             tree.menu.parentType = cn.xtype;
560             tree.menu.triggerEl = cn.el;
561             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
562             
563         }
564         */
565         if (!tree.items || !tree.items.length) {
566             cn.items = nitems;
567             //Roo.log(["no children", this]);
568             
569             return cn;
570         }
571          
572         var items = tree.items;
573         delete tree.items;
574         
575         //Roo.log(items.length);
576             // add the items..
577         if (!skip_children) {    
578             for(var i =0;i < items.length;i++) {
579               //  Roo.log(['add child', items[i]]);
580                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
581             }
582         }
583         
584         cn.items = nitems;
585         
586         //Roo.log("fire childrenrendered");
587         
588         cn.fireEvent('childrenrendered', this);
589         
590         return cn;
591     },
592     
593     /**
594      * Set the element that will be used to show or hide
595      */
596     setVisibilityEl : function(el)
597     {
598         this.visibilityEl = el;
599     },
600     
601      /**
602      * Get the element that will be used to show or hide
603      */
604     getVisibilityEl : function()
605     {
606         if (typeof(this.visibilityEl) == 'object') {
607             return this.visibilityEl;
608         }
609         
610         if (typeof(this.visibilityEl) == 'string') {
611             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612         }
613         
614         return this.getEl();
615     },
616     
617     /**
618      * Show a component - removes 'hidden' class
619      */
620     show : function()
621     {
622         if(!this.getVisibilityEl()){
623             return;
624         }
625          
626         this.getVisibilityEl().removeClass(['hidden','d-none']);
627         
628         this.fireEvent('show', this);
629         
630         
631     },
632     /**
633      * Hide a component - adds 'hidden' class
634      */
635     hide: function()
636     {
637         if(!this.getVisibilityEl()){
638             return;
639         }
640         
641         this.getVisibilityEl().addClass(['hidden','d-none']);
642         
643         this.fireEvent('hide', this);
644         
645     }
646 });
647
648  /*
649  * - LGPL
650  *
651  * element
652  * 
653  */
654
655 /**
656  * @class Roo.bootstrap.Element
657  * @extends Roo.bootstrap.Component
658  * @children Roo.bootstrap.Component
659  * Bootstrap Element class (basically a DIV used to make random stuff )
660  * 
661  * @cfg {String} html contents of the element
662  * @cfg {String} tag tag of the element
663  * @cfg {String} cls class of the element
664  * @cfg {Boolean} preventDefault (true|false) default false
665  * @cfg {Boolean} clickable (true|false) default false
666  * @cfg {String} role default blank - set to button to force cursor pointer
667  
668  * 
669  * @constructor
670  * Create a new Element
671  * @param {Object} config The config object
672  */
673
674 Roo.bootstrap.Element = function(config){
675     Roo.bootstrap.Element.superclass.constructor.call(this, config);
676     
677     this.addEvents({
678         // raw events
679         /**
680          * @event click
681          * When a element is chick
682          * @param {Roo.bootstrap.Element} this
683          * @param {Roo.EventObject} e
684          */
685         "click" : true 
686         
687       
688     });
689 };
690
691 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
692     
693     tag: 'div',
694     cls: '',
695     html: '',
696     preventDefault: false, 
697     clickable: false,
698     tapedTwice : false,
699     role : false,
700     
701     getAutoCreate : function(){
702         
703         var cfg = {
704             tag: this.tag,
705             // cls: this.cls, double assign in parent class Component.js :: onRender
706             html: this.html
707         };
708         if (this.role !== false) {
709             cfg.role = this.role;
710         }
711         
712         return cfg;
713     },
714     
715     initEvents: function() 
716     {
717         Roo.bootstrap.Element.superclass.initEvents.call(this);
718         
719         if(this.clickable){
720             this.el.on('click', this.onClick, this);
721         }
722         
723         
724     },
725     
726     onClick : function(e)
727     {
728         if(this.preventDefault){
729             e.preventDefault();
730         }
731         
732         this.fireEvent('click', this, e); // why was this double click before?
733     },
734     
735     
736     
737
738     
739     
740     getValue : function()
741     {
742         return this.el.dom.innerHTML;
743     },
744     
745     setValue : function(value)
746     {
747         this.el.dom.innerHTML = value;
748     }
749    
750 });
751
752  
753
754  /*
755  * - LGPL
756  *
757  * dropable area
758  * 
759  */
760
761 /**
762  * @class Roo.bootstrap.DropTarget
763  * @extends Roo.bootstrap.Element
764  * Bootstrap DropTarget class
765  
766  * @cfg {string} name dropable name
767  * 
768  * @constructor
769  * Create a new Dropable Area
770  * @param {Object} config The config object
771  */
772
773 Roo.bootstrap.DropTarget = function(config){
774     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
775     
776     this.addEvents({
777         // raw events
778         /**
779          * @event click
780          * When a element is chick
781          * @param {Roo.bootstrap.Element} this
782          * @param {Roo.EventObject} e
783          */
784         "drop" : true
785     });
786 };
787
788 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
789     
790     
791     getAutoCreate : function(){
792         
793          
794     },
795     
796     initEvents: function() 
797     {
798         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
799         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
800             ddGroup: this.name,
801             listeners : {
802                 drop : this.dragDrop.createDelegate(this),
803                 enter : this.dragEnter.createDelegate(this),
804                 out : this.dragOut.createDelegate(this),
805                 over : this.dragOver.createDelegate(this)
806             }
807             
808         });
809         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
810     },
811     
812     dragDrop : function(source,e,data)
813     {
814         // user has to decide how to impliment this.
815         Roo.log('drop');
816         Roo.log(this);
817         //this.fireEvent('drop', this, source, e ,data);
818         return false;
819     },
820     
821     dragEnter : function(n, dd, e, data)
822     {
823         // probably want to resize the element to match the dropped element..
824         Roo.log("enter");
825         this.originalSize = this.el.getSize();
826         this.el.setSize( n.el.getSize());
827         this.dropZone.DDM.refreshCache(this.name);
828         Roo.log([n, dd, e, data]);
829     },
830     
831     dragOut : function(value)
832     {
833         // resize back to normal
834         Roo.log("out");
835         this.el.setSize(this.originalSize);
836         this.dropZone.resetConstraints();
837     },
838     
839     dragOver : function()
840     {
841         // ??? do nothing?
842     }
843    
844 });
845
846  
847
848  /*
849  * - LGPL
850  *
851  * Body
852  *
853  */
854
855 /**
856  * @class Roo.bootstrap.Body
857  * @extends Roo.bootstrap.Component
858  * @children Roo.bootstrap.Component 
859  * @parent none builder
860  * Bootstrap Body class
861  *
862  * @constructor
863  * Create a new body
864  * @param {Object} config The config object
865  */
866
867 Roo.bootstrap.Body = function(config){
868
869     config = config || {};
870
871     Roo.bootstrap.Body.superclass.constructor.call(this, config);
872     this.el = Roo.get(config.el ? config.el : document.body );
873     if (this.cls && this.cls.length) {
874         Roo.get(document.body).addClass(this.cls);
875     }
876 };
877
878 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
879
880     is_body : true,// just to make sure it's constructed?
881
882         autoCreate : {
883         cls: 'container'
884     },
885     onRender : function(ct, position)
886     {
887        /* Roo.log("Roo.bootstrap.Body - onRender");
888         if (this.cls && this.cls.length) {
889             Roo.get(document.body).addClass(this.cls);
890         }
891         // style??? xttr???
892         */
893     }
894
895
896
897
898 });
899 /*
900  * - LGPL
901  *
902  * button group
903  * 
904  */
905
906
907 /**
908  * @class Roo.bootstrap.ButtonGroup
909  * @extends Roo.bootstrap.Component
910  * Bootstrap ButtonGroup class
911  * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
912  * 
913  * @cfg {String} size lg | sm | xs (default empty normal)
914  * @cfg {String} align vertical | justified  (default none)
915  * @cfg {String} direction up | down (default down)
916  * @cfg {Boolean} toolbar false | true
917  * @cfg {Boolean} btn true | false
918  * 
919  * 
920  * @constructor
921  * Create a new Input
922  * @param {Object} config The config object
923  */
924
925 Roo.bootstrap.ButtonGroup = function(config){
926     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
927 };
928
929 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
930     
931     size: '',
932     align: '',
933     direction: '',
934     toolbar: false,
935     btn: true,
936
937     getAutoCreate : function(){
938         var cfg = {
939             cls: 'btn-group',
940             html : null
941         };
942         
943         cfg.html = this.html || cfg.html;
944         
945         if (this.toolbar) {
946             cfg = {
947                 cls: 'btn-toolbar',
948                 html: null
949             };
950             
951             return cfg;
952         }
953         
954         if (['vertical','justified'].indexOf(this.align)!==-1) {
955             cfg.cls = 'btn-group-' + this.align;
956             
957             if (this.align == 'justified') {
958                 console.log(this.items);
959             }
960         }
961         
962         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
963             cfg.cls += ' btn-group-' + this.size;
964         }
965         
966         if (this.direction == 'up') {
967             cfg.cls += ' dropup' ;
968         }
969         
970         return cfg;
971     },
972     /**
973      * Add a button to the group (similar to NavItem API.)
974      */
975     addItem : function(cfg)
976     {
977         var cn = new Roo.bootstrap.Button(cfg);
978         //this.register(cn);
979         cn.parentId = this.id;
980         cn.onRender(this.el, null);
981         return cn;
982     }
983    
984 });
985
986  /*
987  * - LGPL
988  *
989  * button
990  * 
991  */
992
993 /**
994  * @class Roo.bootstrap.Button
995  * @extends Roo.bootstrap.Component
996  * Bootstrap Button class
997  * @cfg {String} html The button content
998  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
999  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1000  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1001  * @cfg {String} size (lg|sm|xs)
1002  * @cfg {String} tag (a|input|submit)
1003  * @cfg {String} href empty or href
1004  * @cfg {Boolean} disabled default false;
1005  * @cfg {Boolean} isClose default false;
1006  * @cfg {String} glyphicon depricated - use fa
1007  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1008  * @cfg {String} badge text for badge
1009  * @cfg {String} theme (default|glow)  
1010  * @cfg {Boolean} inverse dark themed version
1011  * @cfg {Boolean} toggle is it a slidy toggle button
1012  * @cfg {Boolean} pressed   default null - if the button ahs active state
1013  * @cfg {String} ontext text for on slidy toggle state
1014  * @cfg {String} offtext text for off slidy toggle state
1015  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1016  * @cfg {Boolean} removeClass remove the standard class..
1017  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1018  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1019  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1020
1021  * @constructor
1022  * Create a new button
1023  * @param {Object} config The config object
1024  */
1025
1026
1027 Roo.bootstrap.Button = function(config){
1028     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1029     
1030     this.addEvents({
1031         // raw events
1032         /**
1033          * @event click
1034          * When a button is pressed
1035          * @param {Roo.bootstrap.Button} btn
1036          * @param {Roo.EventObject} e
1037          */
1038         "click" : true,
1039         /**
1040          * @event dblclick
1041          * When a button is double clicked
1042          * @param {Roo.bootstrap.Button} btn
1043          * @param {Roo.EventObject} e
1044          */
1045         "dblclick" : true,
1046          /**
1047          * @event toggle
1048          * After the button has been toggles
1049          * @param {Roo.bootstrap.Button} btn
1050          * @param {Roo.EventObject} e
1051          * @param {boolean} pressed (also available as button.pressed)
1052          */
1053         "toggle" : true
1054     });
1055 };
1056
1057 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1058     html: false,
1059     active: false,
1060     weight: '',
1061     badge_weight: '',
1062     outline : false,
1063     size: '',
1064     tag: 'button',
1065     href: '',
1066     disabled: false,
1067     isClose: false,
1068     glyphicon: '',
1069     fa: '',
1070     badge: '',
1071     theme: 'default',
1072     inverse: false,
1073     
1074     toggle: false,
1075     ontext: 'ON',
1076     offtext: 'OFF',
1077     defaulton: true,
1078     preventDefault: true,
1079     removeClass: false,
1080     name: false,
1081     target: false,
1082     group : false,
1083      
1084     pressed : null,
1085      
1086     
1087     getAutoCreate : function(){
1088         
1089         var cfg = {
1090             tag : 'button',
1091             cls : 'roo-button',
1092             html: ''
1093         };
1094         
1095         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1096             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1097             this.tag = 'button';
1098         } else {
1099             cfg.tag = this.tag;
1100         }
1101         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1102         
1103         if (this.toggle == true) {
1104             cfg={
1105                 tag: 'div',
1106                 cls: 'slider-frame roo-button',
1107                 cn: [
1108                     {
1109                         tag: 'span',
1110                         'data-on-text':'ON',
1111                         'data-off-text':'OFF',
1112                         cls: 'slider-button',
1113                         html: this.offtext
1114                     }
1115                 ]
1116             };
1117             // why are we validating the weights?
1118             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1119                 cfg.cls +=  ' ' + this.weight;
1120             }
1121             
1122             return cfg;
1123         }
1124         
1125         if (this.isClose) {
1126             cfg.cls += ' close';
1127             
1128             cfg["aria-hidden"] = true;
1129             
1130             cfg.html = "&times;";
1131             
1132             return cfg;
1133         }
1134              
1135         
1136         if (this.theme==='default') {
1137             cfg.cls = 'btn roo-button';
1138             
1139             //if (this.parentType != 'Navbar') {
1140             this.weight = this.weight.length ?  this.weight : 'default';
1141             //}
1142             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1143                 
1144                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1145                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1146                 cfg.cls += ' btn-' + outline + weight;
1147                 if (this.weight == 'default') {
1148                     // BC
1149                     cfg.cls += ' btn-' + this.weight;
1150                 }
1151             }
1152         } else if (this.theme==='glow') {
1153             
1154             cfg.tag = 'a';
1155             cfg.cls = 'btn-glow roo-button';
1156             
1157             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1158                 
1159                 cfg.cls += ' ' + this.weight;
1160             }
1161         }
1162    
1163         
1164         if (this.inverse) {
1165             this.cls += ' inverse';
1166         }
1167         
1168         
1169         if (this.active || this.pressed === true) {
1170             cfg.cls += ' active';
1171         }
1172         
1173         if (this.disabled) {
1174             cfg.disabled = 'disabled';
1175         }
1176         
1177         if (this.items) {
1178             Roo.log('changing to ul' );
1179             cfg.tag = 'ul';
1180             this.glyphicon = 'caret';
1181             if (Roo.bootstrap.version == 4) {
1182                 this.fa = 'caret-down';
1183             }
1184             
1185         }
1186         
1187         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1188          
1189         //gsRoo.log(this.parentType);
1190         if (this.parentType === 'Navbar' && !this.parent().bar) {
1191             Roo.log('changing to li?');
1192             
1193             cfg.tag = 'li';
1194             
1195             cfg.cls = '';
1196             cfg.cn =  [{
1197                 tag : 'a',
1198                 cls : 'roo-button',
1199                 html : this.html,
1200                 href : this.href || '#'
1201             }];
1202             if (this.menu) {
1203                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1204                 cfg.cls += ' dropdown';
1205             }   
1206             
1207             delete cfg.html;
1208             
1209         }
1210         
1211        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1212         
1213         if (this.glyphicon) {
1214             cfg.html = ' ' + cfg.html;
1215             
1216             cfg.cn = [
1217                 {
1218                     tag: 'span',
1219                     cls: 'glyphicon glyphicon-' + this.glyphicon
1220                 }
1221             ];
1222         }
1223         if (this.fa) {
1224             cfg.html = ' ' + cfg.html;
1225             
1226             cfg.cn = [
1227                 {
1228                     tag: 'i',
1229                     cls: 'fa fas fa-' + this.fa
1230                 }
1231             ];
1232         }
1233         
1234         if (this.badge) {
1235             cfg.html += ' ';
1236             
1237             cfg.tag = 'a';
1238             
1239 //            cfg.cls='btn roo-button';
1240             
1241             cfg.href=this.href;
1242             
1243             var value = cfg.html;
1244             
1245             if(this.glyphicon){
1246                 value = {
1247                     tag: 'span',
1248                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1249                     html: this.html
1250                 };
1251             }
1252             if(this.fa){
1253                 value = {
1254                     tag: 'i',
1255                     cls: 'fa fas fa-' + this.fa,
1256                     html: this.html
1257                 };
1258             }
1259             
1260             var bw = this.badge_weight.length ? this.badge_weight :
1261                 (this.weight.length ? this.weight : 'secondary');
1262             bw = bw == 'default' ? 'secondary' : bw;
1263             
1264             cfg.cn = [
1265                 value,
1266                 {
1267                     tag: 'span',
1268                     cls: 'badge badge-' + bw,
1269                     html: this.badge
1270                 }
1271             ];
1272             
1273             cfg.html='';
1274         }
1275         
1276         if (this.menu) {
1277             cfg.cls += ' dropdown';
1278             cfg.html = typeof(cfg.html) != 'undefined' ?
1279                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1280         }
1281         
1282         if (cfg.tag !== 'a' && this.href !== '') {
1283             throw "Tag must be a to set href.";
1284         } else if (this.href.length > 0) {
1285             cfg.href = this.href;
1286         }
1287         
1288         if(this.removeClass){
1289             cfg.cls = '';
1290         }
1291         
1292         if(this.target){
1293             cfg.target = this.target;
1294         }
1295         
1296         return cfg;
1297     },
1298     initEvents: function() {
1299        // Roo.log('init events?');
1300 //        Roo.log(this.el.dom);
1301         // add the menu...
1302         
1303         if (typeof (this.menu) != 'undefined') {
1304             this.menu.parentType = this.xtype;
1305             this.menu.triggerEl = this.el;
1306             this.addxtype(Roo.apply({}, this.menu));
1307         }
1308
1309
1310         if (this.el.hasClass('roo-button')) {
1311              this.el.on('click', this.onClick, this);
1312              this.el.on('dblclick', this.onDblClick, this);
1313         } else {
1314              this.el.select('.roo-button').on('click', this.onClick, this);
1315              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1316              
1317         }
1318         // why?
1319         if(this.removeClass){
1320             this.el.on('click', this.onClick, this);
1321         }
1322         
1323         if (this.group === true) {
1324              if (this.pressed === false || this.pressed === true) {
1325                 // nothing
1326             } else {
1327                 this.pressed = false;
1328                 this.setActive(this.pressed);
1329             }
1330             
1331         }
1332         
1333         this.el.enableDisplayMode();
1334         
1335     },
1336     onClick : function(e)
1337     {
1338         if (this.disabled) {
1339             return;
1340         }
1341         
1342         Roo.log('button on click ');
1343         if(this.href === '' || this.preventDefault){
1344             e.preventDefault();
1345         }
1346         
1347         if (this.group) {
1348             if (this.pressed) {
1349                 // do nothing -
1350                 return;
1351             }
1352             this.setActive(true);
1353             var pi = this.parent().items;
1354             for (var i = 0;i < pi.length;i++) {
1355                 if (this == pi[i]) {
1356                     continue;
1357                 }
1358                 if (pi[i].el.hasClass('roo-button')) {
1359                     pi[i].setActive(false);
1360                 }
1361             }
1362             this.fireEvent('click', this, e);            
1363             return;
1364         }
1365         
1366         if (this.pressed === true || this.pressed === false) {
1367             this.toggleActive(e);
1368         }
1369         
1370         
1371         this.fireEvent('click', this, e);
1372     },
1373     onDblClick: function(e)
1374     {
1375         if (this.disabled) {
1376             return;
1377         }
1378         if(this.preventDefault){
1379             e.preventDefault();
1380         }
1381         this.fireEvent('dblclick', this, e);
1382     },
1383     /**
1384      * Enables this button
1385      */
1386     enable : function()
1387     {
1388         this.disabled = false;
1389         this.el.removeClass('disabled');
1390         this.el.dom.removeAttribute("disabled");
1391     },
1392     
1393     /**
1394      * Disable this button
1395      */
1396     disable : function()
1397     {
1398         this.disabled = true;
1399         this.el.addClass('disabled');
1400         this.el.attr("disabled", "disabled")
1401     },
1402      /**
1403      * sets the active state on/off, 
1404      * @param {Boolean} state (optional) Force a particular state
1405      */
1406     setActive : function(v) {
1407         
1408         this.el[v ? 'addClass' : 'removeClass']('active');
1409         this.pressed = v;
1410     },
1411      /**
1412      * toggles the current active state 
1413      */
1414     toggleActive : function(e)
1415     {
1416         this.setActive(!this.pressed); // this modifies pressed...
1417         this.fireEvent('toggle', this, e, this.pressed);
1418     },
1419      /**
1420      * get the current active state
1421      * @return {boolean} true if it's active
1422      */
1423     isActive : function()
1424     {
1425         return this.el.hasClass('active');
1426     },
1427     /**
1428      * set the text of the first selected button
1429      */
1430     setText : function(str)
1431     {
1432         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1433     },
1434     /**
1435      * get the text of the first selected button
1436      */
1437     getText : function()
1438     {
1439         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1440     },
1441     
1442     setWeight : function(str)
1443     {
1444         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1445         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1446         this.weight = str;
1447         var outline = this.outline ? 'outline-' : '';
1448         if (str == 'default') {
1449             this.el.addClass('btn-default btn-outline-secondary');        
1450             return;
1451         }
1452         this.el.addClass('btn-' + outline + str);        
1453     }
1454     
1455     
1456 });
1457 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1458
1459 Roo.bootstrap.Button.weights = [
1460     'default',
1461     'secondary' ,
1462     'primary',
1463     'success',
1464     'info',
1465     'warning',
1466     'danger',
1467     'link',
1468     'light',
1469     'dark'              
1470    
1471 ];/*
1472  * - LGPL
1473  *
1474  * column
1475  * 
1476  */
1477
1478 /**
1479  * @class Roo.bootstrap.Column
1480  * @extends Roo.bootstrap.Component
1481  * @children Roo.bootstrap.Component
1482  * Bootstrap Column class
1483  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1484  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1485  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1486  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1487  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1488  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1489  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1490  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1491  *
1492  * 
1493  * @cfg {Boolean} hidden (true|false) hide the element
1494  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1495  * @cfg {String} fa (ban|check|...) font awesome icon
1496  * @cfg {Number} fasize (1|2|....) font awsome size
1497
1498  * @cfg {String} icon (info-sign|check|...) glyphicon name
1499
1500  * @cfg {String} html content of column.
1501  * 
1502  * @constructor
1503  * Create a new Column
1504  * @param {Object} config The config object
1505  */
1506
1507 Roo.bootstrap.Column = function(config){
1508     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1509 };
1510
1511 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1512     
1513     xs: false,
1514     sm: false,
1515     md: false,
1516     lg: false,
1517     xsoff: false,
1518     smoff: false,
1519     mdoff: false,
1520     lgoff: false,
1521     html: '',
1522     offset: 0,
1523     alert: false,
1524     fa: false,
1525     icon : false,
1526     hidden : false,
1527     fasize : 1,
1528     
1529     getAutoCreate : function(){
1530         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1531         
1532         cfg = {
1533             tag: 'div',
1534             cls: 'column'
1535         };
1536         
1537         var settings=this;
1538         var sizes =   ['xs','sm','md','lg'];
1539         sizes.map(function(size ,ix){
1540             //Roo.log( size + ':' + settings[size]);
1541             
1542             if (settings[size+'off'] !== false) {
1543                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1544             }
1545             
1546             if (settings[size] === false) {
1547                 return;
1548             }
1549             
1550             if (!settings[size]) { // 0 = hidden
1551                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1552                 // bootsrap4
1553                 for (var i = ix; i > -1; i--) {
1554                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1555                 }
1556                 
1557                 
1558                 return;
1559             }
1560             cfg.cls += ' col-' + size + '-' + settings[size] + (
1561                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1562             );
1563             
1564         });
1565         
1566         if (this.hidden) {
1567             cfg.cls += ' hidden';
1568         }
1569         
1570         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1571             cfg.cls +=' alert alert-' + this.alert;
1572         }
1573         
1574         
1575         if (this.html.length) {
1576             cfg.html = this.html;
1577         }
1578         if (this.fa) {
1579             var fasize = '';
1580             if (this.fasize > 1) {
1581                 fasize = ' fa-' + this.fasize + 'x';
1582             }
1583             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1584             
1585             
1586         }
1587         if (this.icon) {
1588             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1589         }
1590         
1591         return cfg;
1592     }
1593    
1594 });
1595
1596  
1597
1598  /*
1599  * - LGPL
1600  *
1601  * page container.
1602  * 
1603  */
1604
1605
1606 /**
1607  * @class Roo.bootstrap.Container
1608  * @extends Roo.bootstrap.Component
1609  * @children Roo.bootstrap.Component
1610  * @parent builder
1611  * Bootstrap Container class
1612  * @cfg {Boolean} jumbotron is it a jumbotron element
1613  * @cfg {String} html content of element
1614  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1615  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1616  * @cfg {String} header content of header (for panel)
1617  * @cfg {String} footer content of footer (for panel)
1618  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1619  * @cfg {String} tag (header|aside|section) type of HTML tag.
1620  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1621  * @cfg {String} fa font awesome icon
1622  * @cfg {String} icon (info-sign|check|...) glyphicon name
1623  * @cfg {Boolean} hidden (true|false) hide the element
1624  * @cfg {Boolean} expandable (true|false) default false
1625  * @cfg {Boolean} expanded (true|false) default true
1626  * @cfg {String} rheader contet on the right of header
1627  * @cfg {Boolean} clickable (true|false) default false
1628
1629  *     
1630  * @constructor
1631  * Create a new Container
1632  * @param {Object} config The config object
1633  */
1634
1635 Roo.bootstrap.Container = function(config){
1636     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1637     
1638     this.addEvents({
1639         // raw events
1640          /**
1641          * @event expand
1642          * After the panel has been expand
1643          * 
1644          * @param {Roo.bootstrap.Container} this
1645          */
1646         "expand" : true,
1647         /**
1648          * @event collapse
1649          * After the panel has been collapsed
1650          * 
1651          * @param {Roo.bootstrap.Container} this
1652          */
1653         "collapse" : true,
1654         /**
1655          * @event click
1656          * When a element is chick
1657          * @param {Roo.bootstrap.Container} this
1658          * @param {Roo.EventObject} e
1659          */
1660         "click" : true
1661     });
1662 };
1663
1664 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1665     
1666     jumbotron : false,
1667     well: '',
1668     panel : '',
1669     header: '',
1670     footer : '',
1671     sticky: '',
1672     tag : false,
1673     alert : false,
1674     fa: false,
1675     icon : false,
1676     expandable : false,
1677     rheader : '',
1678     expanded : true,
1679     clickable: false,
1680   
1681      
1682     getChildContainer : function() {
1683         
1684         if(!this.el){
1685             return false;
1686         }
1687         
1688         if (this.panel.length) {
1689             return this.el.select('.panel-body',true).first();
1690         }
1691         
1692         return this.el;
1693     },
1694     
1695     
1696     getAutoCreate : function(){
1697         
1698         var cfg = {
1699             tag : this.tag || 'div',
1700             html : '',
1701             cls : ''
1702         };
1703         if (this.jumbotron) {
1704             cfg.cls = 'jumbotron';
1705         }
1706         
1707         
1708         
1709         // - this is applied by the parent..
1710         //if (this.cls) {
1711         //    cfg.cls = this.cls + '';
1712         //}
1713         
1714         if (this.sticky.length) {
1715             
1716             var bd = Roo.get(document.body);
1717             if (!bd.hasClass('bootstrap-sticky')) {
1718                 bd.addClass('bootstrap-sticky');
1719                 Roo.select('html',true).setStyle('height', '100%');
1720             }
1721              
1722             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1723         }
1724         
1725         
1726         if (this.well.length) {
1727             switch (this.well) {
1728                 case 'lg':
1729                 case 'sm':
1730                     cfg.cls +=' well well-' +this.well;
1731                     break;
1732                 default:
1733                     cfg.cls +=' well';
1734                     break;
1735             }
1736         }
1737         
1738         if (this.hidden) {
1739             cfg.cls += ' hidden';
1740         }
1741         
1742         
1743         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1744             cfg.cls +=' alert alert-' + this.alert;
1745         }
1746         
1747         var body = cfg;
1748         
1749         if (this.panel.length) {
1750             cfg.cls += ' panel panel-' + this.panel;
1751             cfg.cn = [];
1752             if (this.header.length) {
1753                 
1754                 var h = [];
1755                 
1756                 if(this.expandable){
1757                     
1758                     cfg.cls = cfg.cls + ' expandable';
1759                     
1760                     h.push({
1761                         tag: 'i',
1762                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1763                     });
1764                     
1765                 }
1766                 
1767                 h.push(
1768                     {
1769                         tag: 'span',
1770                         cls : 'panel-title',
1771                         html : (this.expandable ? '&nbsp;' : '') + this.header
1772                     },
1773                     {
1774                         tag: 'span',
1775                         cls: 'panel-header-right',
1776                         html: this.rheader
1777                     }
1778                 );
1779                 
1780                 cfg.cn.push({
1781                     cls : 'panel-heading',
1782                     style : this.expandable ? 'cursor: pointer' : '',
1783                     cn : h
1784                 });
1785                 
1786             }
1787             
1788             body = false;
1789             cfg.cn.push({
1790                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1791                 html : this.html
1792             });
1793             
1794             
1795             if (this.footer.length) {
1796                 cfg.cn.push({
1797                     cls : 'panel-footer',
1798                     html : this.footer
1799                     
1800                 });
1801             }
1802             
1803         }
1804         
1805         if (body) {
1806             body.html = this.html || cfg.html;
1807             // prefix with the icons..
1808             if (this.fa) {
1809                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1810             }
1811             if (this.icon) {
1812                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1813             }
1814             
1815             
1816         }
1817         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1818             cfg.cls =  'container';
1819         }
1820         
1821         return cfg;
1822     },
1823     
1824     initEvents: function() 
1825     {
1826         if(this.expandable){
1827             var headerEl = this.headerEl();
1828         
1829             if(headerEl){
1830                 headerEl.on('click', this.onToggleClick, this);
1831             }
1832         }
1833         
1834         if(this.clickable){
1835             this.el.on('click', this.onClick, this);
1836         }
1837         
1838     },
1839     
1840     onToggleClick : function()
1841     {
1842         var headerEl = this.headerEl();
1843         
1844         if(!headerEl){
1845             return;
1846         }
1847         
1848         if(this.expanded){
1849             this.collapse();
1850             return;
1851         }
1852         
1853         this.expand();
1854     },
1855     
1856     expand : function()
1857     {
1858         if(this.fireEvent('expand', this)) {
1859             
1860             this.expanded = true;
1861             
1862             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1863             
1864             this.el.select('.panel-body',true).first().removeClass('hide');
1865             
1866             var toggleEl = this.toggleEl();
1867
1868             if(!toggleEl){
1869                 return;
1870             }
1871
1872             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1873         }
1874         
1875     },
1876     
1877     collapse : function()
1878     {
1879         if(this.fireEvent('collapse', this)) {
1880             
1881             this.expanded = false;
1882             
1883             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1884             this.el.select('.panel-body',true).first().addClass('hide');
1885         
1886             var toggleEl = this.toggleEl();
1887
1888             if(!toggleEl){
1889                 return;
1890             }
1891
1892             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1893         }
1894     },
1895     
1896     toggleEl : function()
1897     {
1898         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1899             return;
1900         }
1901         
1902         return this.el.select('.panel-heading .fa',true).first();
1903     },
1904     
1905     headerEl : function()
1906     {
1907         if(!this.el || !this.panel.length || !this.header.length){
1908             return;
1909         }
1910         
1911         return this.el.select('.panel-heading',true).first()
1912     },
1913     
1914     bodyEl : function()
1915     {
1916         if(!this.el || !this.panel.length){
1917             return;
1918         }
1919         
1920         return this.el.select('.panel-body',true).first()
1921     },
1922     
1923     titleEl : function()
1924     {
1925         if(!this.el || !this.panel.length || !this.header.length){
1926             return;
1927         }
1928         
1929         return this.el.select('.panel-title',true).first();
1930     },
1931     
1932     setTitle : function(v)
1933     {
1934         var titleEl = this.titleEl();
1935         
1936         if(!titleEl){
1937             return;
1938         }
1939         
1940         titleEl.dom.innerHTML = v;
1941     },
1942     
1943     getTitle : function()
1944     {
1945         
1946         var titleEl = this.titleEl();
1947         
1948         if(!titleEl){
1949             return '';
1950         }
1951         
1952         return titleEl.dom.innerHTML;
1953     },
1954     
1955     setRightTitle : function(v)
1956     {
1957         var t = this.el.select('.panel-header-right',true).first();
1958         
1959         if(!t){
1960             return;
1961         }
1962         
1963         t.dom.innerHTML = v;
1964     },
1965     
1966     onClick : function(e)
1967     {
1968         e.preventDefault();
1969         
1970         this.fireEvent('click', this, e);
1971     }
1972 });
1973
1974  /**
1975  * @class Roo.bootstrap.Card
1976  * @extends Roo.bootstrap.Component
1977  * @children Roo.bootstrap.Component
1978  * @licence LGPL
1979  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1980  *
1981  *
1982  * possible... may not be implemented..
1983  * @cfg {String} header_image  src url of image.
1984  * @cfg {String|Object} header
1985  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1986  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1987  * 
1988  * @cfg {String} title
1989  * @cfg {String} subtitle
1990  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1991  * @cfg {String} footer
1992  
1993  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1994  * 
1995  * @cfg {String} margin (0|1|2|3|4|5|auto)
1996  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1997  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2000  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2001  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2002  *
2003  * @cfg {String} padding (0|1|2|3|4|5)
2004  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2005  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2006  * @cfg {String} padding_left (0|1|2|3|4|5)
2007  * @cfg {String} padding_right (0|1|2|3|4|5)
2008  * @cfg {String} padding_x (0|1|2|3|4|5)
2009  * @cfg {String} padding_y (0|1|2|3|4|5)
2010  *
2011  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016  
2017  * @config {Boolean} dragable  if this card can be dragged.
2018  * @config {String} drag_group  group for drag
2019  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2020  * @config {String} drop_group  group for drag
2021  * 
2022  * @config {Boolean} collapsable can the body be collapsed.
2023  * @config {Boolean} collapsed is the body collapsed when rendered...
2024  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2025  * @config {Boolean} rotated is the body rotated when rendered...
2026  * 
2027  * @constructor
2028  * Create a new Container
2029  * @param {Object} config The config object
2030  */
2031
2032 Roo.bootstrap.Card = function(config){
2033     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2034     
2035     this.addEvents({
2036          // raw events
2037         /**
2038          * @event drop
2039          * When a element a card is dropped
2040          * @param {Roo.bootstrap.Card} this
2041          *
2042          * 
2043          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2044          * @param {String} position 'above' or 'below'
2045          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2046         
2047          */
2048         'drop' : true,
2049          /**
2050          * @event rotate
2051          * When a element a card is rotate
2052          * @param {Roo.bootstrap.Card} this
2053          * @param {Roo.Element} n the node being dropped?
2054          * @param {Boolean} rotate status
2055          */
2056         'rotate' : true,
2057         /**
2058          * @event cardover
2059          * When a card element is dragged over ready to drop (return false to block dropable)
2060          * @param {Roo.bootstrap.Card} this
2061          * @param {Object} data from dragdrop 
2062          */
2063          'cardover' : true
2064          
2065     });
2066 };
2067
2068
2069 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2070     
2071     
2072     weight : '',
2073     
2074     margin: '', /// may be better in component?
2075     margin_top: '', 
2076     margin_bottom: '', 
2077     margin_left: '',
2078     margin_right: '',
2079     margin_x: '',
2080     margin_y: '',
2081     
2082     padding : '',
2083     padding_top: '', 
2084     padding_bottom: '', 
2085     padding_left: '',
2086     padding_right: '',
2087     padding_x: '',
2088     padding_y: '',
2089     
2090     display: '', 
2091     display_xs: '', 
2092     display_sm: '', 
2093     display_lg: '',
2094     display_xl: '',
2095  
2096     header_image  : '',
2097     header : '',
2098     header_size : 0,
2099     title : '',
2100     subtitle : '',
2101     html : '',
2102     footer: '',
2103
2104     collapsable : false,
2105     collapsed : false,
2106     rotateable : false,
2107     rotated : false,
2108     
2109     dragable : false,
2110     drag_group : false,
2111     dropable : false,
2112     drop_group : false,
2113     childContainer : false,
2114     dropEl : false, /// the dom placeholde element that indicates drop location.
2115     containerEl: false, // body container
2116     bodyEl: false, // card-body
2117     headerContainerEl : false, //
2118     headerEl : false,
2119     header_imageEl : false,
2120     
2121     
2122     layoutCls : function()
2123     {
2124         var cls = '';
2125         var t = this;
2126         Roo.log(this.margin_bottom.length);
2127         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2128             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2129             
2130             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2131                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2132             }
2133             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2134                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2135             }
2136         });
2137         
2138         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2139             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2140                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2141             }
2142         });
2143         
2144         // more generic support?
2145         if (this.hidden) {
2146             cls += ' d-none';
2147         }
2148         
2149         return cls;
2150     },
2151  
2152        // Roo.log("Call onRender: " + this.xtype);
2153         /*  We are looking at something like this.
2154 <div class="card">
2155     <img src="..." class="card-img-top" alt="...">
2156     <div class="card-body">
2157         <h5 class="card-title">Card title</h5>
2158          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2159
2160         >> this bit is really the body...
2161         <div> << we will ad dthis in hopefully it will not break shit.
2162         
2163         ** card text does not actually have any styling...
2164         
2165             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2166         
2167         </div> <<
2168           <a href="#" class="card-link">Card link</a>
2169           
2170     </div>
2171     <div class="card-footer">
2172         <small class="text-muted">Last updated 3 mins ago</small>
2173     </div>
2174 </div>
2175          */
2176     getAutoCreate : function(){
2177         
2178         var cfg = {
2179             tag : 'div',
2180             cls : 'card',
2181             cn : [ ]
2182         };
2183         
2184         if (this.weight.length && this.weight != 'light') {
2185             cfg.cls += ' text-white';
2186         } else {
2187             cfg.cls += ' text-dark'; // need as it's nested..
2188         }
2189         if (this.weight.length) {
2190             cfg.cls += ' bg-' + this.weight;
2191         }
2192         
2193         cfg.cls += ' ' + this.layoutCls(); 
2194         
2195         var hdr = false;
2196         var hdr_ctr = false;
2197         if (this.header.length) {
2198             hdr = {
2199                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2200                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2201                 cn : []
2202             };
2203             cfg.cn.push(hdr);
2204             hdr_ctr = hdr;
2205         } else {
2206             hdr = {
2207                 tag : 'div',
2208                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2209                 cn : []
2210             };
2211             cfg.cn.push(hdr);
2212             hdr_ctr = hdr;
2213         }
2214         if (this.collapsable) {
2215             hdr_ctr = {
2216             tag : 'a',
2217             cls : 'd-block user-select-none',
2218             cn: [
2219                     {
2220                         tag: 'i',
2221                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2222                     }
2223                    
2224                 ]
2225             };
2226             hdr.cn.push(hdr_ctr);
2227         }
2228         
2229         hdr_ctr.cn.push(        {
2230             tag: 'span',
2231             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2232             html : this.header
2233         });
2234         
2235         
2236         if (this.header_image.length) {
2237             cfg.cn.push({
2238                 tag : 'img',
2239                 cls : 'card-img-top',
2240                 src: this.header_image // escape?
2241             });
2242         } else {
2243             cfg.cn.push({
2244                     tag : 'div',
2245                     cls : 'card-img-top d-none' 
2246                 });
2247         }
2248             
2249         var body = {
2250             tag : 'div',
2251             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2252             cn : []
2253         };
2254         var obody = body;
2255         if (this.collapsable || this.rotateable) {
2256             obody = {
2257                 tag: 'div',
2258                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2259                 cn : [  body ]
2260             };
2261         }
2262         
2263         cfg.cn.push(obody);
2264         
2265         if (this.title.length) {
2266             body.cn.push({
2267                 tag : 'div',
2268                 cls : 'card-title',
2269                 src: this.title // escape?
2270             });
2271         }  
2272         
2273         if (this.subtitle.length) {
2274             body.cn.push({
2275                 tag : 'div',
2276                 cls : 'card-title',
2277                 src: this.subtitle // escape?
2278             });
2279         }
2280         
2281         body.cn.push({
2282             tag : 'div',
2283             cls : 'roo-card-body-ctr'
2284         });
2285         
2286         if (this.html.length) {
2287             body.cn.push({
2288                 tag: 'div',
2289                 html : this.html
2290             });
2291         }
2292         // fixme ? handle objects?
2293         
2294         if (this.footer.length) {
2295            
2296             cfg.cn.push({
2297                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2298                 html : this.footer
2299             });
2300             
2301         } else {
2302             cfg.cn.push({cls : 'card-footer d-none'});
2303         }
2304         
2305         // footer...
2306         
2307         return cfg;
2308     },
2309     
2310     
2311     getCardHeader : function()
2312     {
2313         var  ret = this.el.select('.card-header',true).first();
2314         if (ret.hasClass('d-none')) {
2315             ret.removeClass('d-none');
2316         }
2317         
2318         return ret;
2319     },
2320     getCardFooter : function()
2321     {
2322         var  ret = this.el.select('.card-footer',true).first();
2323         if (ret.hasClass('d-none')) {
2324             ret.removeClass('d-none');
2325         }
2326         
2327         return ret;
2328     },
2329     getCardImageTop : function()
2330     {
2331         var  ret = this.header_imageEl;
2332         if (ret.hasClass('d-none')) {
2333             ret.removeClass('d-none');
2334         }
2335             
2336         return ret;
2337     },
2338     
2339     getChildContainer : function()
2340     {
2341         
2342         if(!this.el){
2343             return false;
2344         }
2345         return this.el.select('.roo-card-body-ctr',true).first();    
2346     },
2347     
2348     initEvents: function() 
2349     {
2350         this.bodyEl = this.el.select('.card-body',true).first(); 
2351         this.containerEl = this.getChildContainer();
2352         if(this.dragable){
2353             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2354                     containerScroll: true,
2355                     ddGroup: this.drag_group || 'default_card_drag_group'
2356             });
2357             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2358         }
2359         if (this.dropable) {
2360             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2361                 containerScroll: true,
2362                 ddGroup: this.drop_group || 'default_card_drag_group'
2363             });
2364             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2365             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2366             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2367             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2368             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2369         }
2370         
2371         if (this.collapsable) {
2372             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2373         }
2374         if (this.rotateable) {
2375             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2376         }
2377         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2378          
2379         this.footerEl = this.el.select('.card-footer',true).first();
2380         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2381         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2382         this.headerEl = this.el.select('.card-header',true).first();
2383         
2384         if (this.rotated) {
2385             this.el.addClass('roo-card-rotated');
2386             this.fireEvent('rotate', this, true);
2387         }
2388         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2389         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2390         
2391     },
2392     getDragData : function(e)
2393     {
2394         var target = this.getEl();
2395         if (target) {
2396             //this.handleSelection(e);
2397             
2398             var dragData = {
2399                 source: this,
2400                 copy: false,
2401                 nodes: this.getEl(),
2402                 records: []
2403             };
2404             
2405             
2406             dragData.ddel = target.dom ;    // the div element
2407             Roo.log(target.getWidth( ));
2408             dragData.ddel.style.width = target.getWidth() + 'px';
2409             
2410             return dragData;
2411         }
2412         return false;
2413     },
2414     /**
2415     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2416     *    whole Element becomes the target, and this causes the drop gesture to append.
2417     *
2418     *    Returns an object:
2419     *     {
2420            
2421            position : 'below' or 'above'
2422            card  : relateive to card OBJECT (or true for no cards listed)
2423            items_n : relative to nth item in list
2424            card_n : relative to  nth card in list
2425     }
2426     *
2427     *    
2428     */
2429     getTargetFromEvent : function(e, dragged_card_el)
2430     {
2431         var target = e.getTarget();
2432         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2433             target = target.parentNode;
2434         }
2435         
2436         var ret = {
2437             position: '',
2438             cards : [],
2439             card_n : -1,
2440             items_n : -1,
2441             card : false 
2442         };
2443         
2444         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2445         // see if target is one of the 'cards'...
2446         
2447         
2448         //Roo.log(this.items.length);
2449         var pos = false;
2450         
2451         var last_card_n = 0;
2452         var cards_len  = 0;
2453         for (var i = 0;i< this.items.length;i++) {
2454             
2455             if (!this.items[i].el.hasClass('card')) {
2456                  continue;
2457             }
2458             pos = this.getDropPoint(e, this.items[i].el.dom);
2459             
2460             cards_len = ret.cards.length;
2461             //Roo.log(this.items[i].el.dom.id);
2462             ret.cards.push(this.items[i]);
2463             last_card_n  = i;
2464             if (ret.card_n < 0 && pos == 'above') {
2465                 ret.position = cards_len > 0 ? 'below' : pos;
2466                 ret.items_n = i > 0 ? i - 1 : 0;
2467                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2468                 ret.card = ret.cards[ret.card_n];
2469             }
2470         }
2471         if (!ret.cards.length) {
2472             ret.card = true;
2473             ret.position = 'below';
2474             ret.items_n;
2475             return ret;
2476         }
2477         // could not find a card.. stick it at the end..
2478         if (ret.card_n < 0) {
2479             ret.card_n = last_card_n;
2480             ret.card = ret.cards[last_card_n];
2481             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2482             ret.position = 'below';
2483         }
2484         
2485         if (this.items[ret.items_n].el == dragged_card_el) {
2486             return false;
2487         }
2488         
2489         if (ret.position == 'below') {
2490             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2491             
2492             if (card_after  && card_after.el == dragged_card_el) {
2493                 return false;
2494             }
2495             return ret;
2496         }
2497         
2498         // its's after ..
2499         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2500         
2501         if (card_before  && card_before.el == dragged_card_el) {
2502             return false;
2503         }
2504         
2505         return ret;
2506     },
2507     
2508     onNodeEnter : function(n, dd, e, data){
2509         return false;
2510     },
2511     onNodeOver : function(n, dd, e, data)
2512     {
2513        
2514         var target_info = this.getTargetFromEvent(e,data.source.el);
2515         if (target_info === false) {
2516             this.dropPlaceHolder('hide');
2517             return false;
2518         }
2519         Roo.log(['getTargetFromEvent', target_info ]);
2520         
2521         
2522         if (this.fireEvent('cardover', this, [ data ]) === false) {
2523             return false;
2524         }
2525         
2526         this.dropPlaceHolder('show', target_info,data);
2527         
2528         return false; 
2529     },
2530     onNodeOut : function(n, dd, e, data){
2531         this.dropPlaceHolder('hide');
2532      
2533     },
2534     onNodeDrop : function(n, dd, e, data)
2535     {
2536         
2537         // call drop - return false if
2538         
2539         // this could actually fail - if the Network drops..
2540         // we will ignore this at present..- client should probably reload
2541         // the whole set of cards if stuff like that fails.
2542         
2543         
2544         var info = this.getTargetFromEvent(e,data.source.el);
2545         if (info === false) {
2546             return false;
2547         }
2548         this.dropPlaceHolder('hide');
2549   
2550           
2551     
2552         this.acceptCard(data.source, info.position, info.card, info.items_n);
2553         return true;
2554          
2555     },
2556     firstChildCard : function()
2557     {
2558         for (var i = 0;i< this.items.length;i++) {
2559             
2560             if (!this.items[i].el.hasClass('card')) {
2561                  continue;
2562             }
2563             return this.items[i];
2564         }
2565         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2566     },
2567     /**
2568      * accept card
2569      *
2570      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2571      */
2572     acceptCard : function(move_card,  position, next_to_card )
2573     {
2574         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2575             return false;
2576         }
2577         
2578         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2579         
2580         move_card.parent().removeCard(move_card);
2581         
2582         
2583         var dom = move_card.el.dom;
2584         dom.style.width = ''; // clear with - which is set by drag.
2585         
2586         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2587             var cardel = next_to_card.el.dom;
2588             
2589             if (position == 'above' ) {
2590                 cardel.parentNode.insertBefore(dom, cardel);
2591             } else if (cardel.nextSibling) {
2592                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2593             } else {
2594                 cardel.parentNode.append(dom);
2595             }
2596         } else {
2597             // card container???
2598             this.containerEl.dom.append(dom);
2599         }
2600         
2601         //FIXME HANDLE card = true 
2602         
2603         // add this to the correct place in items.
2604         
2605         // remove Card from items.
2606         
2607        
2608         if (this.items.length) {
2609             var nitems = [];
2610             //Roo.log([info.items_n, info.position, this.items.length]);
2611             for (var i =0; i < this.items.length; i++) {
2612                 if (i == to_items_n && position == 'above') {
2613                     nitems.push(move_card);
2614                 }
2615                 nitems.push(this.items[i]);
2616                 if (i == to_items_n && position == 'below') {
2617                     nitems.push(move_card);
2618                 }
2619             }
2620             this.items = nitems;
2621             Roo.log(this.items);
2622         } else {
2623             this.items.push(move_card);
2624         }
2625         
2626         move_card.parentId = this.id;
2627         
2628         return true;
2629         
2630         
2631     },
2632     removeCard : function(c)
2633     {
2634         this.items = this.items.filter(function(e) { return e != c });
2635  
2636         var dom = c.el.dom;
2637         dom.parentNode.removeChild(dom);
2638         dom.style.width = ''; // clear with - which is set by drag.
2639         c.parentId = false;
2640         
2641     },
2642     
2643     /**    Decide whether to drop above or below a View node. */
2644     getDropPoint : function(e, n, dd)
2645     {
2646         if (dd) {
2647              return false;
2648         }
2649         if (n == this.containerEl.dom) {
2650             return "above";
2651         }
2652         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2653         var c = t + (b - t) / 2;
2654         var y = Roo.lib.Event.getPageY(e);
2655         if(y <= c) {
2656             return "above";
2657         }else{
2658             return "below";
2659         }
2660     },
2661     onToggleCollapse : function(e)
2662         {
2663         if (this.collapsed) {
2664             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2665             this.collapsableEl.addClass('show');
2666             this.collapsed = false;
2667             return;
2668         }
2669         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2670         this.collapsableEl.removeClass('show');
2671         this.collapsed = true;
2672         
2673     
2674     },
2675     
2676     onToggleRotate : function(e)
2677     {
2678         this.collapsableEl.removeClass('show');
2679         this.footerEl.removeClass('d-none');
2680         this.el.removeClass('roo-card-rotated');
2681         this.el.removeClass('d-none');
2682         if (this.rotated) {
2683             
2684             this.collapsableEl.addClass('show');
2685             this.rotated = false;
2686             this.fireEvent('rotate', this, this.rotated);
2687             return;
2688         }
2689         this.el.addClass('roo-card-rotated');
2690         this.footerEl.addClass('d-none');
2691         this.el.select('.roo-collapsable').removeClass('show');
2692         
2693         this.rotated = true;
2694         this.fireEvent('rotate', this, this.rotated);
2695     
2696     },
2697     
2698     dropPlaceHolder: function (action, info, data)
2699     {
2700         if (this.dropEl === false) {
2701             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2702             cls : 'd-none'
2703             },true);
2704         }
2705         this.dropEl.removeClass(['d-none', 'd-block']);        
2706         if (action == 'hide') {
2707             
2708             this.dropEl.addClass('d-none');
2709             return;
2710         }
2711         // FIXME - info.card == true!!!
2712         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2713         
2714         if (info.card !== true) {
2715             var cardel = info.card.el.dom;
2716             
2717             if (info.position == 'above') {
2718                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2719             } else if (cardel.nextSibling) {
2720                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2721             } else {
2722                 cardel.parentNode.append(this.dropEl.dom);
2723             }
2724         } else {
2725             // card container???
2726             this.containerEl.dom.append(this.dropEl.dom);
2727         }
2728         
2729         this.dropEl.addClass('d-block roo-card-dropzone');
2730         
2731         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2732         
2733         
2734     
2735     
2736     
2737     },
2738     setHeaderText: function(html)
2739     {
2740         this.header = html;
2741         if (this.headerContainerEl) {
2742             this.headerContainerEl.dom.innerHTML = html;
2743         }
2744     },
2745     onHeaderImageLoad : function(ev, he)
2746     {
2747         if (!this.header_image_fit_square) {
2748             return;
2749         }
2750         
2751         var hw = he.naturalHeight / he.naturalWidth;
2752         // wide image = < 0
2753         // tall image = > 1
2754         //var w = he.dom.naturalWidth;
2755         var ww = he.width;
2756         he.style.left =  0;
2757         he.style.position =  'relative';
2758         if (hw > 1) {
2759             var nw = (ww * (1/hw));
2760             Roo.get(he).setSize( ww * (1/hw),  ww);
2761             he.style.left =  ((ww - nw)/ 2) + 'px';
2762             he.style.position =  'relative';
2763         }
2764
2765     }
2766
2767     
2768 });
2769
2770 /*
2771  * - LGPL
2772  *
2773  * Card header - holder for the card header elements.
2774  * 
2775  */
2776
2777 /**
2778  * @class Roo.bootstrap.CardHeader
2779  * @extends Roo.bootstrap.Element
2780  * @parent Roo.bootstrap.Card
2781  * @children Roo.bootstrap.Component
2782  * Bootstrap CardHeader class
2783  * @constructor
2784  * Create a new Card Header - that you can embed children into
2785  * @param {Object} config The config object
2786  */
2787
2788 Roo.bootstrap.CardHeader = function(config){
2789     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2790 };
2791
2792 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2793     
2794     
2795     container_method : 'getCardHeader' 
2796     
2797      
2798     
2799     
2800    
2801 });
2802
2803  
2804
2805  /*
2806  * - LGPL
2807  *
2808  * Card footer - holder for the card footer elements.
2809  * 
2810  */
2811
2812 /**
2813  * @class Roo.bootstrap.CardFooter
2814  * @extends Roo.bootstrap.Element
2815  * @parent Roo.bootstrap.Card
2816  * @children Roo.bootstrap.Component
2817  * Bootstrap CardFooter class
2818  * 
2819  * @constructor
2820  * Create a new Card Footer - that you can embed children into
2821  * @param {Object} config The config object
2822  */
2823
2824 Roo.bootstrap.CardFooter = function(config){
2825     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2826 };
2827
2828 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2829     
2830     
2831     container_method : 'getCardFooter' 
2832     
2833      
2834     
2835     
2836    
2837 });
2838
2839  
2840
2841  /*
2842  * - LGPL
2843  *
2844  * Card header - holder for the card header elements.
2845  * 
2846  */
2847
2848 /**
2849  * @class Roo.bootstrap.CardImageTop
2850  * @extends Roo.bootstrap.Element
2851  * @parent Roo.bootstrap.Card
2852  * @children Roo.bootstrap.Component
2853  * Bootstrap CardImageTop class
2854  * 
2855  * @constructor
2856  * Create a new Card Image Top container
2857  * @param {Object} config The config object
2858  */
2859
2860 Roo.bootstrap.CardImageTop = function(config){
2861     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2862 };
2863
2864 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2865     
2866    
2867     container_method : 'getCardImageTop' 
2868     
2869      
2870     
2871    
2872 });
2873
2874  
2875
2876  
2877 /*
2878 * Licence: LGPL
2879 */
2880
2881 /**
2882  * @class Roo.bootstrap.ButtonUploader
2883  * @extends Roo.bootstrap.Button
2884  * Bootstrap Button Uploader class - it's a button which when you add files to it
2885  *
2886  * 
2887  * @cfg {Number} errorTimeout default 3000
2888  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2889  * @cfg {Array}  html The button text.
2890  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2891  *
2892  * @constructor
2893  * Create a new CardUploader
2894  * @param {Object} config The config object
2895  */
2896
2897 Roo.bootstrap.ButtonUploader = function(config){
2898     
2899  
2900     
2901     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2902     
2903      
2904      this.addEvents({
2905          // raw events
2906         /**
2907          * @event beforeselect
2908          * When button is pressed, before show upload files dialog is shown
2909          * @param {Roo.bootstrap.UploaderButton} this
2910          *
2911          */
2912         'beforeselect' : true,
2913          /**
2914          * @event fired when files have been selected, 
2915          * When a the download link is clicked
2916          * @param {Roo.bootstrap.UploaderButton} this
2917          * @param {Array} Array of files that have been uploaded
2918          */
2919         'uploaded' : true
2920         
2921     });
2922 };
2923  
2924 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2925     
2926      
2927     errorTimeout : 3000,
2928      
2929     images : false,
2930    
2931     fileCollection : false,
2932     allowBlank : true,
2933     
2934     multiple : true,
2935     
2936     getAutoCreate : function()
2937     {
2938        
2939         
2940         return  {
2941             cls :'div' ,
2942             cn : [
2943                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this) 
2944             ]
2945         };
2946            
2947          
2948     },
2949      
2950    
2951     initEvents : function()
2952     {
2953         
2954         Roo.bootstrap.Button.prototype.initEvents.call(this);
2955         
2956         
2957         
2958         
2959         
2960         this.urlAPI = (window.createObjectURL && window) || 
2961                                 (window.URL && URL.revokeObjectURL && URL) || 
2962                                 (window.webkitURL && webkitURL);
2963                         
2964         var im = {
2965             tag: 'input',
2966             type : 'file',
2967             cls : 'd-none  roo-card-upload-selector' 
2968           
2969         };
2970         if (this.multiple) {
2971             im.multiple = 'multiple';
2972         }
2973         this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2974        
2975         //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2976         
2977         this.selectorEl.on('change', this.onFileSelected, this);
2978          
2979          
2980        
2981     },
2982     
2983    
2984     onClick : function(e)
2985     {
2986         e.preventDefault();
2987         
2988         if ( this.fireEvent('beforeselect', this) === false) {
2989             return;
2990         }
2991          
2992         this.selectorEl.dom.click();
2993          
2994     },
2995     
2996     onFileSelected : function(e)
2997     {
2998         e.preventDefault();
2999         
3000         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3001             return;
3002         }
3003         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3004         this.selectorEl.dom.value  = '';// hopefully reset..
3005         
3006         this.fireEvent('uploaded', this,  files );
3007         
3008     },
3009     
3010        
3011    
3012     
3013     /**
3014      * addCard - add an Attachment to the uploader
3015      * @param data - the data about the image to upload
3016      *
3017      * {
3018           id : 123
3019           title : "Title of file",
3020           is_uploaded : false,
3021           src : "http://.....",
3022           srcfile : { the File upload object },
3023           mimetype : file.type,
3024           preview : false,
3025           is_deleted : 0
3026           .. any other data...
3027         }
3028      *
3029      * 
3030     */
3031      
3032     reset: function()
3033     {
3034          
3035          this.selectorEl
3036     } 
3037     
3038     
3039     
3040     
3041 });
3042  /*
3043  * - LGPL
3044  *
3045  * image
3046  * 
3047  */
3048
3049
3050 /**
3051  * @class Roo.bootstrap.Img
3052  * @extends Roo.bootstrap.Component
3053  * Bootstrap Img class
3054  * @cfg {Boolean} imgResponsive false | true
3055  * @cfg {String} border rounded | circle | thumbnail
3056  * @cfg {String} src image source
3057  * @cfg {String} alt image alternative text
3058  * @cfg {String} href a tag href
3059  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3060  * @cfg {String} xsUrl xs image source
3061  * @cfg {String} smUrl sm image source
3062  * @cfg {String} mdUrl md image source
3063  * @cfg {String} lgUrl lg image source
3064  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3065  * 
3066  * @constructor
3067  * Create a new Input
3068  * @param {Object} config The config object
3069  */
3070
3071 Roo.bootstrap.Img = function(config){
3072     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3073     
3074     this.addEvents({
3075         // img events
3076         /**
3077          * @event click
3078          * The img click event for the img.
3079          * @param {Roo.EventObject} e
3080          */
3081         "click" : true,
3082         /**
3083          * @event load
3084          * The when any image loads
3085          * @param {Roo.EventObject} e
3086          */
3087         "load" : true
3088     });
3089 };
3090
3091 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3092     
3093     imgResponsive: true,
3094     border: '',
3095     src: 'about:blank',
3096     href: false,
3097     target: false,
3098     xsUrl: '',
3099     smUrl: '',
3100     mdUrl: '',
3101     lgUrl: '',
3102     backgroundContain : false,
3103
3104     getAutoCreate : function()
3105     {   
3106         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3107             return this.createSingleImg();
3108         }
3109         
3110         var cfg = {
3111             tag: 'div',
3112             cls: 'roo-image-responsive-group',
3113             cn: []
3114         };
3115         var _this = this;
3116         
3117         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3118             
3119             if(!_this[size + 'Url']){
3120                 return;
3121             }
3122             
3123             var img = {
3124                 tag: 'img',
3125                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3126                 html: _this.html || cfg.html,
3127                 src: _this[size + 'Url']
3128             };
3129             
3130             img.cls += ' roo-image-responsive-' + size;
3131             
3132             var s = ['xs', 'sm', 'md', 'lg'];
3133             
3134             s.splice(s.indexOf(size), 1);
3135             
3136             Roo.each(s, function(ss){
3137                 img.cls += ' hidden-' + ss;
3138             });
3139             
3140             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3141                 cfg.cls += ' img-' + _this.border;
3142             }
3143             
3144             if(_this.alt){
3145                 cfg.alt = _this.alt;
3146             }
3147             
3148             if(_this.href){
3149                 var a = {
3150                     tag: 'a',
3151                     href: _this.href,
3152                     cn: [
3153                         img
3154                     ]
3155                 };
3156
3157                 if(this.target){
3158                     a.target = _this.target;
3159                 }
3160             }
3161             
3162             cfg.cn.push((_this.href) ? a : img);
3163             
3164         });
3165         
3166         return cfg;
3167     },
3168     
3169     createSingleImg : function()
3170     {
3171         var cfg = {
3172             tag: 'img',
3173             cls: (this.imgResponsive) ? 'img-responsive' : '',
3174             html : null,
3175             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3176         };
3177         
3178         if (this.backgroundContain) {
3179             cfg.cls += ' background-contain';
3180         }
3181         
3182         cfg.html = this.html || cfg.html;
3183         
3184         if (this.backgroundContain) {
3185             cfg.style="background-image: url(" + this.src + ')';
3186         } else {
3187             cfg.src = this.src || cfg.src;
3188         }
3189         
3190         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3191             cfg.cls += ' img-' + this.border;
3192         }
3193         
3194         if(this.alt){
3195             cfg.alt = this.alt;
3196         }
3197         
3198         if(this.href){
3199             var a = {
3200                 tag: 'a',
3201                 href: this.href,
3202                 cn: [
3203                     cfg
3204                 ]
3205             };
3206             
3207             if(this.target){
3208                 a.target = this.target;
3209             }
3210             
3211         }
3212         
3213         return (this.href) ? a : cfg;
3214     },
3215     
3216     initEvents: function() 
3217     {
3218         if(!this.href){
3219             this.el.on('click', this.onClick, this);
3220         }
3221         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3222             this.el.on('load', this.onImageLoad, this);
3223         } else {
3224             // not sure if this works.. not tested
3225             this.el.select('img', true).on('load', this.onImageLoad, this);
3226         }
3227         
3228     },
3229     
3230     onClick : function(e)
3231     {
3232         Roo.log('img onclick');
3233         this.fireEvent('click', this, e);
3234     },
3235     onImageLoad: function(e)
3236     {
3237         Roo.log('img load');
3238         this.fireEvent('load', this, e);
3239     },
3240     
3241     /**
3242      * Sets the url of the image - used to update it
3243      * @param {String} url the url of the image
3244      */
3245     
3246     setSrc : function(url)
3247     {
3248         this.src =  url;
3249         
3250         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3251             if (this.backgroundContain) {
3252                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3253             } else {
3254                 this.el.dom.src =  url;
3255             }
3256             return;
3257         }
3258         
3259         this.el.select('img', true).first().dom.src =  url;
3260     }
3261     
3262     
3263    
3264 });
3265
3266  /*
3267  * - LGPL
3268  *
3269  * image
3270  * 
3271  */
3272
3273
3274 /**
3275  * @class Roo.bootstrap.Link
3276  * @extends Roo.bootstrap.Component
3277  * @children Roo.bootstrap.Component
3278  * Bootstrap Link Class (eg. '<a href>')
3279  
3280  * @cfg {String} alt image alternative text
3281  * @cfg {String} href a tag href
3282  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3283  * @cfg {String} html the content of the link.
3284  * @cfg {String} anchor name for the anchor link
3285  * @cfg {String} fa - favicon
3286
3287  * @cfg {Boolean} preventDefault (true | false) default false
3288
3289  * 
3290  * @constructor
3291  * Create a new Input
3292  * @param {Object} config The config object
3293  */
3294
3295 Roo.bootstrap.Link = function(config){
3296     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3297     
3298     this.addEvents({
3299         // img events
3300         /**
3301          * @event click
3302          * The img click event for the img.
3303          * @param {Roo.EventObject} e
3304          */
3305         "click" : true
3306     });
3307 };
3308
3309 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3310     
3311     href: false,
3312     target: false,
3313     preventDefault: false,
3314     anchor : false,
3315     alt : false,
3316     fa: false,
3317
3318
3319     getAutoCreate : function()
3320     {
3321         var html = this.html || '';
3322         
3323         if (this.fa !== false) {
3324             html = '<i class="fa fa-' + this.fa + '"></i>';
3325         }
3326         var cfg = {
3327             tag: 'a'
3328         };
3329         // anchor's do not require html/href...
3330         if (this.anchor === false) {
3331             cfg.html = html;
3332             cfg.href = this.href || '#';
3333         } else {
3334             cfg.name = this.anchor;
3335             if (this.html !== false || this.fa !== false) {
3336                 cfg.html = html;
3337             }
3338             if (this.href !== false) {
3339                 cfg.href = this.href;
3340             }
3341         }
3342         
3343         if(this.alt !== false){
3344             cfg.alt = this.alt;
3345         }
3346         
3347         
3348         if(this.target !== false) {
3349             cfg.target = this.target;
3350         }
3351         
3352         return cfg;
3353     },
3354     
3355     initEvents: function() {
3356         
3357         if(!this.href || this.preventDefault){
3358             this.el.on('click', this.onClick, this);
3359         }
3360     },
3361     
3362     onClick : function(e)
3363     {
3364         if(this.preventDefault){
3365             e.preventDefault();
3366         }
3367         //Roo.log('img onclick');
3368         this.fireEvent('click', this, e);
3369     }
3370    
3371 });
3372
3373  /*
3374  * - LGPL
3375  *
3376  * header
3377  * 
3378  */
3379
3380 /**
3381  * @class Roo.bootstrap.Header
3382  * @extends Roo.bootstrap.Component
3383  * @children Roo.bootstrap.Component
3384  * Bootstrap Header class
3385  *
3386  * 
3387  * @cfg {String} html content of header
3388  * @cfg {Number} level (1|2|3|4|5|6) default 1
3389  * 
3390  * @constructor
3391  * Create a new Header
3392  * @param {Object} config The config object
3393  */
3394
3395
3396 Roo.bootstrap.Header  = function(config){
3397     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3398 };
3399
3400 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3401     
3402     //href : false,
3403     html : false,
3404     level : 1,
3405     
3406     
3407     
3408     getAutoCreate : function(){
3409         
3410         
3411         
3412         var cfg = {
3413             tag: 'h' + (1 *this.level),
3414             html: this.html || ''
3415         } ;
3416         
3417         return cfg;
3418     }
3419    
3420 });
3421
3422  
3423
3424  /**
3425  * @class Roo.bootstrap.MenuMgr
3426  * @licence LGPL
3427  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3428  * @static
3429  */
3430 Roo.bootstrap.menu.Manager = function(){
3431    var menus, active, groups = {}, attached = false, lastShow = new Date();
3432
3433    // private - called when first menu is created
3434    function init(){
3435        menus = {};
3436        active = new Roo.util.MixedCollection();
3437        Roo.get(document).addKeyListener(27, function(){
3438            if(active.length > 0){
3439                hideAll();
3440            }
3441        });
3442    }
3443
3444    // private
3445    function hideAll(){
3446        if(active && active.length > 0){
3447            var c = active.clone();
3448            c.each(function(m){
3449                m.hide();
3450            });
3451        }
3452    }
3453
3454    // private
3455    function onHide(m){
3456        active.remove(m);
3457        if(active.length < 1){
3458            Roo.get(document).un("mouseup", onMouseDown);
3459             
3460            attached = false;
3461        }
3462    }
3463
3464    // private
3465    function onShow(m){
3466        var last = active.last();
3467        lastShow = new Date();
3468        active.add(m);
3469        if(!attached){
3470           Roo.get(document).on("mouseup", onMouseDown);
3471            
3472            attached = true;
3473        }
3474        if(m.parentMenu){
3475           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3476           m.parentMenu.activeChild = m;
3477        }else if(last && last.isVisible()){
3478           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3479        }
3480    }
3481
3482    // private
3483    function onBeforeHide(m){
3484        if(m.activeChild){
3485            m.activeChild.hide();
3486        }
3487        if(m.autoHideTimer){
3488            clearTimeout(m.autoHideTimer);
3489            delete m.autoHideTimer;
3490        }
3491    }
3492
3493    // private
3494    function onBeforeShow(m){
3495        var pm = m.parentMenu;
3496        if(!pm && !m.allowOtherMenus){
3497            hideAll();
3498        }else if(pm && pm.activeChild && active != m){
3499            pm.activeChild.hide();
3500        }
3501    }
3502
3503    // private this should really trigger on mouseup..
3504    function onMouseDown(e){
3505         Roo.log("on Mouse Up");
3506         
3507         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3508             Roo.log("MenuManager hideAll");
3509             hideAll();
3510             e.stopEvent();
3511         }
3512         
3513         
3514    }
3515
3516    // private
3517    function onBeforeCheck(mi, state){
3518        if(state){
3519            var g = groups[mi.group];
3520            for(var i = 0, l = g.length; i < l; i++){
3521                if(g[i] != mi){
3522                    g[i].setChecked(false);
3523                }
3524            }
3525        }
3526    }
3527
3528    return {
3529
3530        /**
3531         * Hides all menus that are currently visible
3532         */
3533        hideAll : function(){
3534             hideAll();  
3535        },
3536
3537        // private
3538        register : function(menu){
3539            if(!menus){
3540                init();
3541            }
3542            menus[menu.id] = menu;
3543            menu.on("beforehide", onBeforeHide);
3544            menu.on("hide", onHide);
3545            menu.on("beforeshow", onBeforeShow);
3546            menu.on("show", onShow);
3547            var g = menu.group;
3548            if(g && menu.events["checkchange"]){
3549                if(!groups[g]){
3550                    groups[g] = [];
3551                }
3552                groups[g].push(menu);
3553                menu.on("checkchange", onCheck);
3554            }
3555        },
3556
3557         /**
3558          * Returns a {@link Roo.menu.Menu} object
3559          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3560          * be used to generate and return a new Menu instance.
3561          */
3562        get : function(menu){
3563            if(typeof menu == "string"){ // menu id
3564                return menus[menu];
3565            }else if(menu.events){  // menu instance
3566                return menu;
3567            }
3568            /*else if(typeof menu.length == 'number'){ // array of menu items?
3569                return new Roo.bootstrap.Menu({items:menu});
3570            }else{ // otherwise, must be a config
3571                return new Roo.bootstrap.Menu(menu);
3572            }
3573            */
3574            return false;
3575        },
3576
3577        // private
3578        unregister : function(menu){
3579            delete menus[menu.id];
3580            menu.un("beforehide", onBeforeHide);
3581            menu.un("hide", onHide);
3582            menu.un("beforeshow", onBeforeShow);
3583            menu.un("show", onShow);
3584            var g = menu.group;
3585            if(g && menu.events["checkchange"]){
3586                groups[g].remove(menu);
3587                menu.un("checkchange", onCheck);
3588            }
3589        },
3590
3591        // private
3592        registerCheckable : function(menuItem){
3593            var g = menuItem.group;
3594            if(g){
3595                if(!groups[g]){
3596                    groups[g] = [];
3597                }
3598                groups[g].push(menuItem);
3599                menuItem.on("beforecheckchange", onBeforeCheck);
3600            }
3601        },
3602
3603        // private
3604        unregisterCheckable : function(menuItem){
3605            var g = menuItem.group;
3606            if(g){
3607                groups[g].remove(menuItem);
3608                menuItem.un("beforecheckchange", onBeforeCheck);
3609            }
3610        }
3611    };
3612 }(); 
3613 /**
3614  * @class Roo.bootstrap.menu.Menu
3615  * @extends Roo.bootstrap.Component
3616  * @licence LGPL
3617  * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3618  * @parent none
3619  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3620  * 
3621  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3622  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3623  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3624  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3625 * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3626 * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3627  
3628  * @constructor
3629  * Create a new Menu
3630  * @param {Object} config The config objectQ
3631  */
3632
3633
3634 Roo.bootstrap.menu.Menu = function(config){
3635     
3636     if (config.type == 'treeview') {
3637         // normally menu's are drawn attached to the document to handle layering etc..
3638         // however treeview (used by the docs menu is drawn into the parent element)
3639         this.container_method = 'getChildContainer'; 
3640     }
3641     
3642     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3643     if (this.registerMenu && this.type != 'treeview')  {
3644         Roo.bootstrap.menu.Manager.register(this);
3645     }
3646     
3647     
3648     this.addEvents({
3649         /**
3650          * @event beforeshow
3651          * Fires before this menu is displayed (return false to block)
3652          * @param {Roo.menu.Menu} this
3653          */
3654         beforeshow : true,
3655         /**
3656          * @event beforehide
3657          * Fires before this menu is hidden (return false to block)
3658          * @param {Roo.menu.Menu} this
3659          */
3660         beforehide : true,
3661         /**
3662          * @event show
3663          * Fires after this menu is displayed
3664          * @param {Roo.menu.Menu} this
3665          */
3666         show : true,
3667         /**
3668          * @event hide
3669          * Fires after this menu is hidden
3670          * @param {Roo.menu.Menu} this
3671          */
3672         hide : true,
3673         /**
3674          * @event click
3675          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3676          * @param {Roo.menu.Menu} this
3677          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3678          * @param {Roo.EventObject} e
3679          */
3680         click : true,
3681         /**
3682          * @event mouseover
3683          * Fires when the mouse is hovering over this menu
3684          * @param {Roo.menu.Menu} this
3685          * @param {Roo.EventObject} e
3686          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3687          */
3688         mouseover : true,
3689         /**
3690          * @event mouseout
3691          * Fires when the mouse exits this menu
3692          * @param {Roo.menu.Menu} this
3693          * @param {Roo.EventObject} e
3694          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3695          */
3696         mouseout : true,
3697         /**
3698          * @event itemclick
3699          * Fires when a menu item contained in this menu is clicked
3700          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3701          * @param {Roo.EventObject} e
3702          */
3703         itemclick: true
3704     });
3705     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3706 };
3707
3708 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3709     
3710    /// html : false,
3711    
3712     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3713     type: false,
3714     /**
3715      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3716      */
3717     registerMenu : true,
3718     
3719     menuItems :false, // stores the menu items..
3720     
3721     hidden:true,
3722         
3723     parentMenu : false,
3724     
3725     stopEvent : true,
3726     
3727     isLink : false,
3728     
3729     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3730     
3731     hideTrigger : false,
3732     
3733     align : 'tl-bl?',
3734     
3735     
3736     getChildContainer : function() {
3737         return this.el;  
3738     },
3739     
3740     getAutoCreate : function(){
3741          
3742         //if (['right'].indexOf(this.align)!==-1) {
3743         //    cfg.cn[1].cls += ' pull-right'
3744         //}
3745          
3746         var cfg = {
3747             tag : 'ul',
3748             cls : 'dropdown-menu shadow' ,
3749             style : 'z-index:1000'
3750             
3751         };
3752         
3753         if (this.type === 'submenu') {
3754             cfg.cls = 'submenu active';
3755         }
3756         if (this.type === 'treeview') {
3757             cfg.cls = 'treeview-menu';
3758         }
3759         
3760         return cfg;
3761     },
3762     initEvents : function() {
3763         
3764        // Roo.log("ADD event");
3765        // Roo.log(this.triggerEl.dom);
3766         if (this.triggerEl) {
3767             
3768             this.triggerEl.on('click', this.onTriggerClick, this);
3769             
3770             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3771             
3772             if (!this.hideTrigger) {
3773                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3774                     // dropdown toggle on the 'a' in BS4?
3775                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3776                 } else {
3777                     this.triggerEl.addClass('dropdown-toggle');
3778                 }
3779             }
3780         }
3781         
3782         if (Roo.isTouch) {
3783             this.el.on('touchstart'  , this.onTouch, this);
3784         }
3785         this.el.on('click' , this.onClick, this);
3786
3787         this.el.on("mouseover", this.onMouseOver, this);
3788         this.el.on("mouseout", this.onMouseOut, this);
3789         
3790     },
3791     
3792     findTargetItem : function(e)
3793     {
3794         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3795         if(!t){
3796             return false;
3797         }
3798         //Roo.log(t);         Roo.log(t.id);
3799         if(t && t.id){
3800             //Roo.log(this.menuitems);
3801             return this.menuitems.get(t.id);
3802             
3803             //return this.items.get(t.menuItemId);
3804         }
3805         
3806         return false;
3807     },
3808     
3809     onTouch : function(e) 
3810     {
3811         Roo.log("menu.onTouch");
3812         //e.stopEvent(); this make the user popdown broken
3813         this.onClick(e);
3814     },
3815     
3816     onClick : function(e)
3817     {
3818         Roo.log("menu.onClick");
3819         
3820         var t = this.findTargetItem(e);
3821         if(!t || t.isContainer){
3822             return;
3823         }
3824         Roo.log(e);
3825         /*
3826         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3827             if(t == this.activeItem && t.shouldDeactivate(e)){
3828                 this.activeItem.deactivate();
3829                 delete this.activeItem;
3830                 return;
3831             }
3832             if(t.canActivate){
3833                 this.setActiveItem(t, true);
3834             }
3835             return;
3836             
3837             
3838         }
3839         */
3840        
3841         Roo.log('pass click event');
3842         
3843         t.onClick(e);
3844         
3845         this.fireEvent("click", this, t, e);
3846         
3847         var _this = this;
3848         
3849         if(!t.href.length || t.href == '#'){
3850             (function() { _this.hide(); }).defer(100);
3851         }
3852         
3853     },
3854     
3855     onMouseOver : function(e){
3856         var t  = this.findTargetItem(e);
3857         //Roo.log(t);
3858         //if(t){
3859         //    if(t.canActivate && !t.disabled){
3860         //        this.setActiveItem(t, true);
3861         //    }
3862         //}
3863         
3864         this.fireEvent("mouseover", this, e, t);
3865     },
3866     isVisible : function(){
3867         return !this.hidden;
3868     },
3869     onMouseOut : function(e){
3870         var t  = this.findTargetItem(e);
3871         
3872         //if(t ){
3873         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3874         //        this.activeItem.deactivate();
3875         //        delete this.activeItem;
3876         //    }
3877         //}
3878         this.fireEvent("mouseout", this, e, t);
3879     },
3880     
3881     
3882     /**
3883      * Displays this menu relative to another element
3884      * @param {String/HTMLElement/Roo.Element} element The element to align to
3885      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3886      * the element (defaults to this.defaultAlign)
3887      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3888      */
3889     show : function(el, pos, parentMenu)
3890     {
3891         if (false === this.fireEvent("beforeshow", this)) {
3892             Roo.log("show canceled");
3893             return;
3894         }
3895         this.parentMenu = parentMenu;
3896         if(!this.el){
3897             this.render();
3898         }
3899         this.el.addClass('show'); // show otherwise we do not know how big we are..
3900          
3901         var xy = this.el.getAlignToXY(el, pos);
3902         
3903         // bl-tl << left align  below
3904         // tl-bl << left align 
3905         
3906         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3907             // if it goes to far to the right.. -> align left.
3908             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3909         }
3910         if(xy[0] < 0){
3911             // was left align - go right?
3912             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3913         }
3914         
3915         // goes down the bottom
3916         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3917            xy[1]  < 0 ){
3918             var a = this.align.replace('?', '').split('-');
3919             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3920             
3921         }
3922         
3923         this.showAt(  xy , parentMenu, false);
3924     },
3925      /**
3926      * Displays this menu at a specific xy position
3927      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3928      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3929      */
3930     showAt : function(xy, parentMenu, /* private: */_e){
3931         this.parentMenu = parentMenu;
3932         if(!this.el){
3933             this.render();
3934         }
3935         if(_e !== false){
3936             this.fireEvent("beforeshow", this);
3937             //xy = this.el.adjustForConstraints(xy);
3938         }
3939         
3940         //this.el.show();
3941         this.hideMenuItems();
3942         this.hidden = false;
3943         if (this.triggerEl) {
3944             this.triggerEl.addClass('open');
3945         }
3946         
3947         this.el.addClass('show');
3948         
3949         
3950         
3951         // reassign x when hitting right
3952         
3953         // reassign y when hitting bottom
3954         
3955         // but the list may align on trigger left or trigger top... should it be a properity?
3956         
3957         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3958             this.el.setXY(xy);
3959         }
3960         
3961         this.focus();
3962         this.fireEvent("show", this);
3963     },
3964     
3965     focus : function(){
3966         return;
3967         if(!this.hidden){
3968             this.doFocus.defer(50, this);
3969         }
3970     },
3971
3972     doFocus : function(){
3973         if(!this.hidden){
3974             this.focusEl.focus();
3975         }
3976     },
3977
3978     /**
3979      * Hides this menu and optionally all parent menus
3980      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3981      */
3982     hide : function(deep)
3983     {
3984         if (false === this.fireEvent("beforehide", this)) {
3985             Roo.log("hide canceled");
3986             return;
3987         }
3988         this.hideMenuItems();
3989         if(this.el && this.isVisible()){
3990            
3991             if(this.activeItem){
3992                 this.activeItem.deactivate();
3993                 this.activeItem = null;
3994             }
3995             if (this.triggerEl) {
3996                 this.triggerEl.removeClass('open');
3997             }
3998             
3999             this.el.removeClass('show');
4000             this.hidden = true;
4001             this.fireEvent("hide", this);
4002         }
4003         if(deep === true && this.parentMenu){
4004             this.parentMenu.hide(true);
4005         }
4006     },
4007     
4008     onTriggerClick : function(e)
4009     {
4010         Roo.log('trigger click');
4011         
4012         var target = e.getTarget();
4013         
4014         Roo.log(target.nodeName.toLowerCase());
4015         
4016         if(target.nodeName.toLowerCase() === 'i'){
4017             e.preventDefault();
4018         }
4019         
4020     },
4021     
4022     onTriggerPress  : function(e)
4023     {
4024         Roo.log('trigger press');
4025         //Roo.log(e.getTarget());
4026        // Roo.log(this.triggerEl.dom);
4027        
4028         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4029         var pel = Roo.get(e.getTarget());
4030         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4031             Roo.log('is treeview or dropdown?');
4032             return;
4033         }
4034         
4035         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4036             return;
4037         }
4038         
4039         if (this.isVisible()) {
4040             Roo.log('hide');
4041             this.hide();
4042         } else {
4043             Roo.log('show');
4044             
4045             this.show(this.triggerEl, this.align, false);
4046         }
4047         
4048         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4049             e.stopEvent();
4050         }
4051         
4052     },
4053        
4054     
4055     hideMenuItems : function()
4056     {
4057         Roo.log("hide Menu Items");
4058         if (!this.el) { 
4059             return;
4060         }
4061         
4062         this.el.select('.open',true).each(function(aa) {
4063             
4064             aa.removeClass('open');
4065          
4066         });
4067     },
4068     addxtypeChild : function (tree, cntr) {
4069         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4070           
4071         this.menuitems.add(comp);
4072         return comp;
4073
4074     },
4075     getEl : function()
4076     {
4077         Roo.log(this.el);
4078         return this.el;
4079     },
4080     
4081     clear : function()
4082     {
4083         this.getEl().dom.innerHTML = '';
4084         this.menuitems.clear();
4085     }
4086 });
4087
4088  
4089  /**
4090  * @class Roo.bootstrap.menu.Item
4091  * @extends Roo.bootstrap.Component
4092  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4093  * @parent Roo.bootstrap.menu.Menu
4094  * @licence LGPL
4095  * Bootstrap MenuItem class
4096  * 
4097  * @cfg {String} html the menu label
4098  * @cfg {String} href the link
4099  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4100  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4101  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4102  * @cfg {String} fa favicon to show on left of menu item.
4103  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4104  * 
4105  * 
4106  * @constructor
4107  * Create a new MenuItem
4108  * @param {Object} config The config object
4109  */
4110
4111
4112 Roo.bootstrap.menu.Item = function(config){
4113     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4114     this.addEvents({
4115         // raw events
4116         /**
4117          * @event click
4118          * The raw click event for the entire grid.
4119          * @param {Roo.bootstrap.menu.Item} this
4120          * @param {Roo.EventObject} e
4121          */
4122         "click" : true
4123     });
4124 };
4125
4126 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4127     
4128     href : false,
4129     html : false,
4130     preventDefault: false,
4131     isContainer : false,
4132     active : false,
4133     fa: false,
4134     
4135     getAutoCreate : function(){
4136         
4137         if(this.isContainer){
4138             return {
4139                 tag: 'li',
4140                 cls: 'dropdown-menu-item '
4141             };
4142         }
4143         var ctag = {
4144             tag: 'span',
4145             html: 'Link'
4146         };
4147         
4148         var anc = {
4149             tag : 'a',
4150             cls : 'dropdown-item',
4151             href : '#',
4152             cn : [  ]
4153         };
4154         
4155         if (this.fa !== false) {
4156             anc.cn.push({
4157                 tag : 'i',
4158                 cls : 'fa fa-' + this.fa
4159             });
4160         }
4161         
4162         anc.cn.push(ctag);
4163         
4164         
4165         var cfg= {
4166             tag: 'li',
4167             cls: 'dropdown-menu-item',
4168             cn: [ anc ]
4169         };
4170         if (this.parent().type == 'treeview') {
4171             cfg.cls = 'treeview-menu';
4172         }
4173         if (this.active) {
4174             cfg.cls += ' active';
4175         }
4176         
4177         
4178         
4179         anc.href = this.href || cfg.cn[0].href ;
4180         ctag.html = this.html || cfg.cn[0].html ;
4181         return cfg;
4182     },
4183     
4184     initEvents: function()
4185     {
4186         if (this.parent().type == 'treeview') {
4187             this.el.select('a').on('click', this.onClick, this);
4188         }
4189         
4190         if (this.menu) {
4191             this.menu.parentType = this.xtype;
4192             this.menu.triggerEl = this.el;
4193             this.menu = this.addxtype(Roo.apply({}, this.menu));
4194         }
4195         
4196     },
4197     onClick : function(e)
4198     {
4199         //Roo.log('item on click ');
4200         
4201         if(this.href === false || this.preventDefault){
4202             e.preventDefault();
4203         }
4204         //this.parent().hideMenuItems();
4205         
4206         this.fireEvent('click', this, e);
4207     },
4208     getEl : function()
4209     {
4210         return this.el;
4211     } 
4212 });
4213
4214  
4215
4216  
4217
4218   
4219 /**
4220  * @class Roo.bootstrap.menu.Separator
4221  * @extends Roo.bootstrap.Component
4222  * @licence LGPL
4223  * @parent Roo.bootstrap.menu.Menu
4224  * Bootstrap Separator class
4225  * 
4226  * @constructor
4227  * Create a new Separator
4228  * @param {Object} config The config object
4229  */
4230
4231
4232 Roo.bootstrap.menu.Separator = function(config){
4233     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4234 };
4235
4236 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4237     
4238     getAutoCreate : function(){
4239         var cfg = {
4240             tag : 'li',
4241             cls: 'dropdown-divider divider'
4242         };
4243         
4244         return cfg;
4245     }
4246    
4247 });
4248
4249  
4250
4251  
4252 /*
4253 * Licence: LGPL
4254 */
4255
4256 /**
4257  * @class Roo.bootstrap.Modal
4258  * @extends Roo.bootstrap.Component
4259  * @parent none builder
4260  * @children Roo.bootstrap.Component
4261  * Bootstrap Modal class
4262  * @cfg {String} title Title of dialog
4263  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4264  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4265  * @cfg {Boolean} specificTitle default false
4266  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4267  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4268  * @cfg {Boolean} animate default true
4269  * @cfg {Boolean} allow_close default true
4270  * @cfg {Boolean} fitwindow default false
4271  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4272  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4273  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4274  * @cfg {String} size (sm|lg|xl) default empty
4275  * @cfg {Number} max_width set the max width of modal
4276  * @cfg {Boolean} editableTitle can the title be edited
4277
4278  *
4279  *
4280  * @constructor
4281  * Create a new Modal Dialog
4282  * @param {Object} config The config object
4283  */
4284
4285 Roo.bootstrap.Modal = function(config){
4286     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4287     this.addEvents({
4288         // raw events
4289         /**
4290          * @event btnclick
4291          * The raw btnclick event for the button
4292          * @param {Roo.EventObject} e
4293          */
4294         "btnclick" : true,
4295         /**
4296          * @event resize
4297          * Fire when dialog resize
4298          * @param {Roo.bootstrap.Modal} this
4299          * @param {Roo.EventObject} e
4300          */
4301         "resize" : true,
4302         /**
4303          * @event titlechanged
4304          * Fire when the editable title has been changed
4305          * @param {Roo.bootstrap.Modal} this
4306          * @param {Roo.EventObject} value
4307          */
4308         "titlechanged" : true 
4309         
4310     });
4311     this.buttons = this.buttons || [];
4312
4313     if (this.tmpl) {
4314         this.tmpl = Roo.factory(this.tmpl);
4315     }
4316
4317 };
4318
4319 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4320
4321     title : 'test dialog',
4322
4323     buttons : false,
4324
4325     // set on load...
4326
4327     html: false,
4328
4329     tmp: false,
4330
4331     specificTitle: false,
4332
4333     buttonPosition: 'right',
4334
4335     allow_close : true,
4336
4337     animate : true,
4338
4339     fitwindow: false,
4340     
4341      // private
4342     dialogEl: false,
4343     bodyEl:  false,
4344     footerEl:  false,
4345     titleEl:  false,
4346     closeEl:  false,
4347
4348     size: '',
4349     
4350     max_width: 0,
4351     
4352     max_height: 0,
4353     
4354     fit_content: false,
4355     editableTitle  : false,
4356
4357     onRender : function(ct, position)
4358     {
4359         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4360
4361         if(!this.el){
4362             var cfg = Roo.apply({},  this.getAutoCreate());
4363             cfg.id = Roo.id();
4364             //if(!cfg.name){
4365             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4366             //}
4367             //if (!cfg.name.length) {
4368             //    delete cfg.name;
4369            // }
4370             if (this.cls) {
4371                 cfg.cls += ' ' + this.cls;
4372             }
4373             if (this.style) {
4374                 cfg.style = this.style;
4375             }
4376             this.el = Roo.get(document.body).createChild(cfg, position);
4377         }
4378         //var type = this.el.dom.type;
4379
4380
4381         if(this.tabIndex !== undefined){
4382             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4383         }
4384
4385         this.dialogEl = this.el.select('.modal-dialog',true).first();
4386         this.bodyEl = this.el.select('.modal-body',true).first();
4387         this.closeEl = this.el.select('.modal-header .close', true).first();
4388         this.headerEl = this.el.select('.modal-header',true).first();
4389         this.titleEl = this.el.select('.modal-title',true).first();
4390         this.footerEl = this.el.select('.modal-footer',true).first();
4391
4392         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4393         
4394         //this.el.addClass("x-dlg-modal");
4395
4396         if (this.buttons.length) {
4397             Roo.each(this.buttons, function(bb) {
4398                 var b = Roo.apply({}, bb);
4399                 b.xns = b.xns || Roo.bootstrap;
4400                 b.xtype = b.xtype || 'Button';
4401                 if (typeof(b.listeners) == 'undefined') {
4402                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4403                 }
4404
4405                 var btn = Roo.factory(b);
4406
4407                 btn.render(this.getButtonContainer());
4408
4409             },this);
4410         }
4411         // render the children.
4412         var nitems = [];
4413
4414         if(typeof(this.items) != 'undefined'){
4415             var items = this.items;
4416             delete this.items;
4417
4418             for(var i =0;i < items.length;i++) {
4419                 // we force children not to montor widnow resize  - as we do that for them.
4420                 items[i].monitorWindowResize = false;
4421                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4422             }
4423         }
4424
4425         this.items = nitems;
4426
4427         // where are these used - they used to be body/close/footer
4428
4429
4430         this.initEvents();
4431         //this.el.addClass([this.fieldClass, this.cls]);
4432
4433     },
4434
4435     getAutoCreate : function()
4436     {
4437         // we will default to modal-body-overflow - might need to remove or make optional later.
4438         var bdy = {
4439                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4440                 html : this.html || ''
4441         };
4442
4443         var title = {
4444             tag: 'h5',
4445             cls : 'modal-title',
4446             html : this.title
4447         };
4448
4449         if(this.specificTitle){ // WTF is this?
4450             title = this.title;
4451         }
4452
4453         var header = [];
4454         if (this.allow_close && Roo.bootstrap.version == 3) {
4455             header.push({
4456                 tag: 'button',
4457                 cls : 'close',
4458                 html : '&times'
4459             });
4460         }
4461
4462         header.push(title);
4463
4464         if (this.editableTitle) {
4465             header.push({
4466                 cls: 'form-control roo-editable-title d-none',
4467                 tag: 'input',
4468                 type: 'text'
4469             });
4470         }
4471         
4472         if (this.allow_close && Roo.bootstrap.version == 4) {
4473             header.push({
4474                 tag: 'button',
4475                 cls : 'close',
4476                 html : '&times'
4477             });
4478         }
4479         
4480         var size = '';
4481
4482         if(this.size.length){
4483             size = 'modal-' + this.size;
4484         }
4485         
4486         var footer = Roo.bootstrap.version == 3 ?
4487             {
4488                 cls : 'modal-footer',
4489                 cn : [
4490                     {
4491                         tag: 'div',
4492                         cls: 'btn-' + this.buttonPosition
4493                     }
4494                 ]
4495
4496             } :
4497             {  // BS4 uses mr-auto on left buttons....
4498                 cls : 'modal-footer'
4499             };
4500
4501             
4502
4503         
4504         
4505         var modal = {
4506             cls: "modal",
4507              cn : [
4508                 {
4509                     cls: "modal-dialog " + size,
4510                     cn : [
4511                         {
4512                             cls : "modal-content",
4513                             cn : [
4514                                 {
4515                                     cls : 'modal-header',
4516                                     cn : header
4517                                 },
4518                                 bdy,
4519                                 footer
4520                             ]
4521
4522                         }
4523                     ]
4524
4525                 }
4526             ]
4527         };
4528
4529         if(this.animate){
4530             modal.cls += ' fade';
4531         }
4532
4533         return modal;
4534
4535     },
4536     getChildContainer : function() {
4537
4538          return this.bodyEl;
4539
4540     },
4541     getButtonContainer : function() {
4542         
4543          return Roo.bootstrap.version == 4 ?
4544             this.el.select('.modal-footer',true).first()
4545             : this.el.select('.modal-footer div',true).first();
4546
4547     },
4548     initEvents : function()
4549     {
4550         if (this.allow_close) {
4551             this.closeEl.on('click', this.hide, this);
4552         }
4553         Roo.EventManager.onWindowResize(this.resize, this, true);
4554         if (this.editableTitle) {
4555             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4556             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4557             this.headerEditEl.on('keyup', function(e) {
4558                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4559                         this.toggleHeaderInput(false)
4560                     }
4561                 }, this);
4562             this.headerEditEl.on('blur', function(e) {
4563                 this.toggleHeaderInput(false)
4564             },this);
4565         }
4566
4567     },
4568   
4569
4570     resize : function()
4571     {
4572         this.maskEl.setSize(
4573             Roo.lib.Dom.getViewWidth(true),
4574             Roo.lib.Dom.getViewHeight(true)
4575         );
4576         
4577         if (this.fitwindow) {
4578             
4579            this.dialogEl.setStyle( { 'max-width' : '100%' });
4580             this.setSize(
4581                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4582                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4583             );
4584             return;
4585         }
4586         
4587         if(this.max_width !== 0) {
4588             
4589             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4590             
4591             if(this.height) {
4592                 this.setSize(w, this.height);
4593                 return;
4594             }
4595             
4596             if(this.max_height) {
4597                 this.setSize(w,Math.min(
4598                     this.max_height,
4599                     Roo.lib.Dom.getViewportHeight(true) - 60
4600                 ));
4601                 
4602                 return;
4603             }
4604             
4605             if(!this.fit_content) {
4606                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4607                 return;
4608             }
4609             
4610             this.setSize(w, Math.min(
4611                 60 +
4612                 this.headerEl.getHeight() + 
4613                 this.footerEl.getHeight() + 
4614                 this.getChildHeight(this.bodyEl.dom.childNodes),
4615                 Roo.lib.Dom.getViewportHeight(true) - 60)
4616             );
4617         }
4618         
4619     },
4620
4621     setSize : function(w,h)
4622     {
4623         if (!w && !h) {
4624             return;
4625         }
4626         
4627         this.resizeTo(w,h);
4628         // any layout/border etc.. resize..
4629         (function () {
4630             this.items.forEach( function(e) {
4631                 e.layout ? e.layout() : false;
4632
4633             });
4634         }).defer(100,this);
4635         
4636     },
4637
4638     show : function() {
4639
4640         if (!this.rendered) {
4641             this.render();
4642         }
4643         this.toggleHeaderInput(false);
4644         //this.el.setStyle('display', 'block');
4645         this.el.removeClass('hideing');
4646         this.el.dom.style.display='block';
4647         
4648         Roo.get(document.body).addClass('modal-open');
4649  
4650         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4651             
4652             (function(){
4653                 this.el.addClass('show');
4654                 this.el.addClass('in');
4655             }).defer(50, this);
4656         }else{
4657             this.el.addClass('show');
4658             this.el.addClass('in');
4659         }
4660
4661         // not sure how we can show data in here..
4662         //if (this.tmpl) {
4663         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4664         //}
4665
4666         Roo.get(document.body).addClass("x-body-masked");
4667         
4668         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4669         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4670         this.maskEl.dom.style.display = 'block';
4671         this.maskEl.addClass('show');
4672         
4673         
4674         this.resize();
4675         
4676         this.fireEvent('show', this);
4677
4678         // set zindex here - otherwise it appears to be ignored...
4679         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4680         
4681         
4682         // this is for children that are... layout.Border 
4683         (function () {
4684             this.items.forEach( function(e) {
4685                 e.layout ? e.layout() : false;
4686
4687             });
4688         }).defer(100,this);
4689
4690     },
4691     hide : function()
4692     {
4693         if(this.fireEvent("beforehide", this) !== false){
4694             
4695             this.maskEl.removeClass('show');
4696             
4697             this.maskEl.dom.style.display = '';
4698             Roo.get(document.body).removeClass("x-body-masked");
4699             this.el.removeClass('in');
4700             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4701
4702             if(this.animate){ // why
4703                 this.el.addClass('hideing');
4704                 this.el.removeClass('show');
4705                 (function(){
4706                     if (!this.el.hasClass('hideing')) {
4707                         return; // it's been shown again...
4708                     }
4709                     
4710                     this.el.dom.style.display='';
4711
4712                     Roo.get(document.body).removeClass('modal-open');
4713                     this.el.removeClass('hideing');
4714                 }).defer(150,this);
4715                 
4716             }else{
4717                 this.el.removeClass('show');
4718                 this.el.dom.style.display='';
4719                 Roo.get(document.body).removeClass('modal-open');
4720
4721             }
4722             this.fireEvent('hide', this);
4723         }
4724     },
4725     isVisible : function()
4726     {
4727         
4728         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4729         
4730     },
4731
4732     addButton : function(str, cb)
4733     {
4734
4735
4736         var b = Roo.apply({}, { html : str } );
4737         b.xns = b.xns || Roo.bootstrap;
4738         b.xtype = b.xtype || 'Button';
4739         if (typeof(b.listeners) == 'undefined') {
4740             b.listeners = { click : cb.createDelegate(this)  };
4741         }
4742
4743         var btn = Roo.factory(b);
4744
4745         btn.render(this.getButtonContainer());
4746
4747         return btn;
4748
4749     },
4750
4751     setDefaultButton : function(btn)
4752     {
4753         //this.el.select('.modal-footer').()
4754     },
4755
4756     resizeTo: function(w,h)
4757     {
4758         this.dialogEl.setWidth(w);
4759         
4760         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4761
4762         this.bodyEl.setHeight(h - diff);
4763         
4764         this.fireEvent('resize', this);
4765     },
4766     
4767     setContentSize  : function(w, h)
4768     {
4769
4770     },
4771     onButtonClick: function(btn,e)
4772     {
4773         //Roo.log([a,b,c]);
4774         this.fireEvent('btnclick', btn.name, e);
4775     },
4776      /**
4777      * Set the title of the Dialog
4778      * @param {String} str new Title
4779      */
4780     setTitle: function(str) {
4781         this.titleEl.dom.innerHTML = str;
4782         this.title = str;
4783     },
4784     /**
4785      * Set the body of the Dialog
4786      * @param {String} str new Title
4787      */
4788     setBody: function(str) {
4789         this.bodyEl.dom.innerHTML = str;
4790     },
4791     /**
4792      * Set the body of the Dialog using the template
4793      * @param {Obj} data - apply this data to the template and replace the body contents.
4794      */
4795     applyBody: function(obj)
4796     {
4797         if (!this.tmpl) {
4798             Roo.log("Error - using apply Body without a template");
4799             //code
4800         }
4801         this.tmpl.overwrite(this.bodyEl, obj);
4802     },
4803     
4804     getChildHeight : function(child_nodes)
4805     {
4806         if(
4807             !child_nodes ||
4808             child_nodes.length == 0
4809         ) {
4810             return 0;
4811         }
4812         
4813         var child_height = 0;
4814         
4815         for(var i = 0; i < child_nodes.length; i++) {
4816             
4817             /*
4818             * for modal with tabs...
4819             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4820                 
4821                 var layout_childs = child_nodes[i].childNodes;
4822                 
4823                 for(var j = 0; j < layout_childs.length; j++) {
4824                     
4825                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4826                         
4827                         var layout_body_childs = layout_childs[j].childNodes;
4828                         
4829                         for(var k = 0; k < layout_body_childs.length; k++) {
4830                             
4831                             if(layout_body_childs[k].classList.contains('navbar')) {
4832                                 child_height += layout_body_childs[k].offsetHeight;
4833                                 continue;
4834                             }
4835                             
4836                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4837                                 
4838                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4839                                 
4840                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4841                                     
4842                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4843                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4844                                         continue;
4845                                     }
4846                                     
4847                                 }
4848                                 
4849                             }
4850                             
4851                         }
4852                     }
4853                 }
4854                 continue;
4855             }
4856             */
4857             
4858             child_height += child_nodes[i].offsetHeight;
4859             // Roo.log(child_nodes[i].offsetHeight);
4860         }
4861         
4862         return child_height;
4863     },
4864     toggleHeaderInput : function(is_edit)
4865     {
4866         if (!this.editableTitle) {
4867             return; // not editable.
4868         }
4869         if (is_edit && this.is_header_editing) {
4870             return; // already editing..
4871         }
4872         if (is_edit) {
4873     
4874             this.headerEditEl.dom.value = this.title;
4875             this.headerEditEl.removeClass('d-none');
4876             this.headerEditEl.dom.focus();
4877             this.titleEl.addClass('d-none');
4878             
4879             this.is_header_editing = true;
4880             return
4881         }
4882         // flip back to not editing.
4883         this.title = this.headerEditEl.dom.value;
4884         this.headerEditEl.addClass('d-none');
4885         this.titleEl.removeClass('d-none');
4886         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4887         this.is_header_editing = false;
4888         this.fireEvent('titlechanged', this, this.title);
4889     
4890             
4891         
4892     }
4893
4894 });
4895
4896
4897 Roo.apply(Roo.bootstrap.Modal,  {
4898     /**
4899          * Button config that displays a single OK button
4900          * @type Object
4901          */
4902         OK :  [{
4903             name : 'ok',
4904             weight : 'primary',
4905             html : 'OK'
4906         }],
4907         /**
4908          * Button config that displays Yes and No buttons
4909          * @type Object
4910          */
4911         YESNO : [
4912             {
4913                 name  : 'no',
4914                 html : 'No'
4915             },
4916             {
4917                 name  :'yes',
4918                 weight : 'primary',
4919                 html : 'Yes'
4920             }
4921         ],
4922
4923         /**
4924          * Button config that displays OK and Cancel buttons
4925          * @type Object
4926          */
4927         OKCANCEL : [
4928             {
4929                name : 'cancel',
4930                 html : 'Cancel'
4931             },
4932             {
4933                 name : 'ok',
4934                 weight : 'primary',
4935                 html : 'OK'
4936             }
4937         ],
4938         /**
4939          * Button config that displays Yes, No and Cancel buttons
4940          * @type Object
4941          */
4942         YESNOCANCEL : [
4943             {
4944                 name : 'yes',
4945                 weight : 'primary',
4946                 html : 'Yes'
4947             },
4948             {
4949                 name : 'no',
4950                 html : 'No'
4951             },
4952             {
4953                 name : 'cancel',
4954                 html : 'Cancel'
4955             }
4956         ],
4957         
4958         zIndex : 10001
4959 });
4960
4961 /*
4962  * - LGPL
4963  *
4964  * messagebox - can be used as a replace
4965  * 
4966  */
4967 /**
4968  * @class Roo.MessageBox
4969  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4970  * Example usage:
4971  *<pre><code>
4972 // Basic alert:
4973 Roo.Msg.alert('Status', 'Changes saved successfully.');
4974
4975 // Prompt for user data:
4976 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4977     if (btn == 'ok'){
4978         // process text value...
4979     }
4980 });
4981
4982 // Show a dialog using config options:
4983 Roo.Msg.show({
4984    title:'Save Changes?',
4985    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4986    buttons: Roo.Msg.YESNOCANCEL,
4987    fn: processResult,
4988    animEl: 'elId'
4989 });
4990 </code></pre>
4991  * @static
4992  */
4993 Roo.bootstrap.MessageBox = function(){
4994     var dlg, opt, mask, waitTimer;
4995     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4996     var buttons, activeTextEl, bwidth;
4997
4998     
4999     // private
5000     var handleButton = function(button){
5001         dlg.hide();
5002         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5003     };
5004
5005     // private
5006     var handleHide = function(){
5007         if(opt && opt.cls){
5008             dlg.el.removeClass(opt.cls);
5009         }
5010         //if(waitTimer){
5011         //    Roo.TaskMgr.stop(waitTimer);
5012         //    waitTimer = null;
5013         //}
5014     };
5015
5016     // private
5017     var updateButtons = function(b){
5018         var width = 0;
5019         if(!b){
5020             buttons["ok"].hide();
5021             buttons["cancel"].hide();
5022             buttons["yes"].hide();
5023             buttons["no"].hide();
5024             dlg.footerEl.hide();
5025             
5026             return width;
5027         }
5028         dlg.footerEl.show();
5029         for(var k in buttons){
5030             if(typeof buttons[k] != "function"){
5031                 if(b[k]){
5032                     buttons[k].show();
5033                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5034                     width += buttons[k].el.getWidth()+15;
5035                 }else{
5036                     buttons[k].hide();
5037                 }
5038             }
5039         }
5040         return width;
5041     };
5042
5043     // private
5044     var handleEsc = function(d, k, e){
5045         if(opt && opt.closable !== false){
5046             dlg.hide();
5047         }
5048         if(e){
5049             e.stopEvent();
5050         }
5051     };
5052
5053     return {
5054         /**
5055          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5056          * @return {Roo.BasicDialog} The BasicDialog element
5057          */
5058         getDialog : function(){
5059            if(!dlg){
5060                 dlg = new Roo.bootstrap.Modal( {
5061                     //draggable: true,
5062                     //resizable:false,
5063                     //constraintoviewport:false,
5064                     //fixedcenter:true,
5065                     //collapsible : false,
5066                     //shim:true,
5067                     //modal: true,
5068                 //    width: 'auto',
5069                   //  height:100,
5070                     //buttonAlign:"center",
5071                     closeClick : function(){
5072                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5073                             handleButton("no");
5074                         }else{
5075                             handleButton("cancel");
5076                         }
5077                     }
5078                 });
5079                 dlg.render();
5080                 dlg.on("hide", handleHide);
5081                 mask = dlg.mask;
5082                 //dlg.addKeyListener(27, handleEsc);
5083                 buttons = {};
5084                 this.buttons = buttons;
5085                 var bt = this.buttonText;
5086                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5087                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5088                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5089                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5090                 //Roo.log(buttons);
5091                 bodyEl = dlg.bodyEl.createChild({
5092
5093                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5094                         '<textarea class="roo-mb-textarea"></textarea>' +
5095                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5096                 });
5097                 msgEl = bodyEl.dom.firstChild;
5098                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5099                 textboxEl.enableDisplayMode();
5100                 textboxEl.addKeyListener([10,13], function(){
5101                     if(dlg.isVisible() && opt && opt.buttons){
5102                         if(opt.buttons.ok){
5103                             handleButton("ok");
5104                         }else if(opt.buttons.yes){
5105                             handleButton("yes");
5106                         }
5107                     }
5108                 });
5109                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5110                 textareaEl.enableDisplayMode();
5111                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5112                 progressEl.enableDisplayMode();
5113                 
5114                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5115                 var pf = progressEl.dom.firstChild;
5116                 if (pf) {
5117                     pp = Roo.get(pf.firstChild);
5118                     pp.setHeight(pf.offsetHeight);
5119                 }
5120                 
5121             }
5122             return dlg;
5123         },
5124
5125         /**
5126          * Updates the message box body text
5127          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5128          * the XHTML-compliant non-breaking space character '&amp;#160;')
5129          * @return {Roo.MessageBox} This message box
5130          */
5131         updateText : function(text)
5132         {
5133             if(!dlg.isVisible() && !opt.width){
5134                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5135                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5136             }
5137             msgEl.innerHTML = text || '&#160;';
5138       
5139             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5140             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5141             var w = Math.max(
5142                     Math.min(opt.width || cw , this.maxWidth), 
5143                     Math.max(opt.minWidth || this.minWidth, bwidth)
5144             );
5145             if(opt.prompt){
5146                 activeTextEl.setWidth(w);
5147             }
5148             if(dlg.isVisible()){
5149                 dlg.fixedcenter = false;
5150             }
5151             // to big, make it scroll. = But as usual stupid IE does not support
5152             // !important..
5153             
5154             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5155                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5156                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5157             } else {
5158                 bodyEl.dom.style.height = '';
5159                 bodyEl.dom.style.overflowY = '';
5160             }
5161             if (cw > w) {
5162                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5163             } else {
5164                 bodyEl.dom.style.overflowX = '';
5165             }
5166             
5167             dlg.setContentSize(w, bodyEl.getHeight());
5168             if(dlg.isVisible()){
5169                 dlg.fixedcenter = true;
5170             }
5171             return this;
5172         },
5173
5174         /**
5175          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5176          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5177          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5178          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5179          * @return {Roo.MessageBox} This message box
5180          */
5181         updateProgress : function(value, text){
5182             if(text){
5183                 this.updateText(text);
5184             }
5185             
5186             if (pp) { // weird bug on my firefox - for some reason this is not defined
5187                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5188                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5189             }
5190             return this;
5191         },        
5192
5193         /**
5194          * Returns true if the message box is currently displayed
5195          * @return {Boolean} True if the message box is visible, else false
5196          */
5197         isVisible : function(){
5198             return dlg && dlg.isVisible();  
5199         },
5200
5201         /**
5202          * Hides the message box if it is displayed
5203          */
5204         hide : function(){
5205             if(this.isVisible()){
5206                 dlg.hide();
5207             }  
5208         },
5209
5210         /**
5211          * Displays a new message box, or reinitializes an existing message box, based on the config options
5212          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5213          * The following config object properties are supported:
5214          * <pre>
5215 Property    Type             Description
5216 ----------  ---------------  ------------------------------------------------------------------------------------
5217 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5218                                    closes (defaults to undefined)
5219 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5220                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5221 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5222                                    progress and wait dialogs will ignore this property and always hide the
5223                                    close button as they can only be closed programmatically.
5224 cls               String           A custom CSS class to apply to the message box element
5225 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5226                                    displayed (defaults to 75)
5227 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5228                                    function will be btn (the name of the button that was clicked, if applicable,
5229                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5230                                    Progress and wait dialogs will ignore this option since they do not respond to
5231                                    user actions and can only be closed programmatically, so any required function
5232                                    should be called by the same code after it closes the dialog.
5233 icon              String           A CSS class that provides a background image to be used as an icon for
5234                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5235 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5236 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5237 modal             Boolean          False to allow user interaction with the page while the message box is
5238                                    displayed (defaults to true)
5239 msg               String           A string that will replace the existing message box body text (defaults
5240                                    to the XHTML-compliant non-breaking space character '&#160;')
5241 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5242 progress          Boolean          True to display a progress bar (defaults to false)
5243 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5244 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5245 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5246 title             String           The title text
5247 value             String           The string value to set into the active textbox element if displayed
5248 wait              Boolean          True to display a progress bar (defaults to false)
5249 width             Number           The width of the dialog in pixels
5250 </pre>
5251          *
5252          * Example usage:
5253          * <pre><code>
5254 Roo.Msg.show({
5255    title: 'Address',
5256    msg: 'Please enter your address:',
5257    width: 300,
5258    buttons: Roo.MessageBox.OKCANCEL,
5259    multiline: true,
5260    fn: saveAddress,
5261    animEl: 'addAddressBtn'
5262 });
5263 </code></pre>
5264          * @param {Object} config Configuration options
5265          * @return {Roo.MessageBox} This message box
5266          */
5267         show : function(options)
5268         {
5269             
5270             // this causes nightmares if you show one dialog after another
5271             // especially on callbacks..
5272              
5273             if(this.isVisible()){
5274                 
5275                 this.hide();
5276                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5277                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5278                 Roo.log("New Dialog Message:" +  options.msg )
5279                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5280                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5281                 
5282             }
5283             var d = this.getDialog();
5284             opt = options;
5285             d.setTitle(opt.title || "&#160;");
5286             d.closeEl.setDisplayed(opt.closable !== false);
5287             activeTextEl = textboxEl;
5288             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5289             if(opt.prompt){
5290                 if(opt.multiline){
5291                     textboxEl.hide();
5292                     textareaEl.show();
5293                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5294                         opt.multiline : this.defaultTextHeight);
5295                     activeTextEl = textareaEl;
5296                 }else{
5297                     textboxEl.show();
5298                     textareaEl.hide();
5299                 }
5300             }else{
5301                 textboxEl.hide();
5302                 textareaEl.hide();
5303             }
5304             progressEl.setDisplayed(opt.progress === true);
5305             if (opt.progress) {
5306                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5307             }
5308             this.updateProgress(0);
5309             activeTextEl.dom.value = opt.value || "";
5310             if(opt.prompt){
5311                 dlg.setDefaultButton(activeTextEl);
5312             }else{
5313                 var bs = opt.buttons;
5314                 var db = null;
5315                 if(bs && bs.ok){
5316                     db = buttons["ok"];
5317                 }else if(bs && bs.yes){
5318                     db = buttons["yes"];
5319                 }
5320                 dlg.setDefaultButton(db);
5321             }
5322             bwidth = updateButtons(opt.buttons);
5323             this.updateText(opt.msg);
5324             if(opt.cls){
5325                 d.el.addClass(opt.cls);
5326             }
5327             d.proxyDrag = opt.proxyDrag === true;
5328             d.modal = opt.modal !== false;
5329             d.mask = opt.modal !== false ? mask : false;
5330             if(!d.isVisible()){
5331                 // force it to the end of the z-index stack so it gets a cursor in FF
5332                 document.body.appendChild(dlg.el.dom);
5333                 d.animateTarget = null;
5334                 d.show(options.animEl);
5335             }
5336             return this;
5337         },
5338
5339         /**
5340          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5341          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5342          * and closing the message box when the process is complete.
5343          * @param {String} title The title bar text
5344          * @param {String} msg The message box body text
5345          * @return {Roo.MessageBox} This message box
5346          */
5347         progress : function(title, msg){
5348             this.show({
5349                 title : title,
5350                 msg : msg,
5351                 buttons: false,
5352                 progress:true,
5353                 closable:false,
5354                 minWidth: this.minProgressWidth,
5355                 modal : true
5356             });
5357             return this;
5358         },
5359
5360         /**
5361          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5362          * If a callback function is passed it will be called after the user clicks the button, and the
5363          * id of the button that was clicked will be passed as the only parameter to the callback
5364          * (could also be the top-right close button).
5365          * @param {String} title The title bar text
5366          * @param {String} msg The message box body text
5367          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5368          * @param {Object} scope (optional) The scope of the callback function
5369          * @return {Roo.MessageBox} This message box
5370          */
5371         alert : function(title, msg, fn, scope)
5372         {
5373             this.show({
5374                 title : title,
5375                 msg : msg,
5376                 buttons: this.OK,
5377                 fn: fn,
5378                 closable : false,
5379                 scope : scope,
5380                 modal : true
5381             });
5382             return this;
5383         },
5384
5385         /**
5386          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5387          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5388          * You are responsible for closing the message box when the process is complete.
5389          * @param {String} msg The message box body text
5390          * @param {String} title (optional) The title bar text
5391          * @return {Roo.MessageBox} This message box
5392          */
5393         wait : function(msg, title){
5394             this.show({
5395                 title : title,
5396                 msg : msg,
5397                 buttons: false,
5398                 closable:false,
5399                 progress:true,
5400                 modal:true,
5401                 width:300,
5402                 wait:true
5403             });
5404             waitTimer = Roo.TaskMgr.start({
5405                 run: function(i){
5406                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5407                 },
5408                 interval: 1000
5409             });
5410             return this;
5411         },
5412
5413         /**
5414          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5415          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5416          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5417          * @param {String} title The title bar text
5418          * @param {String} msg The message box body text
5419          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5420          * @param {Object} scope (optional) The scope of the callback function
5421          * @return {Roo.MessageBox} This message box
5422          */
5423         confirm : function(title, msg, fn, scope){
5424             this.show({
5425                 title : title,
5426                 msg : msg,
5427                 buttons: this.YESNO,
5428                 fn: fn,
5429                 scope : scope,
5430                 modal : true
5431             });
5432             return this;
5433         },
5434
5435         /**
5436          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5437          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5438          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5439          * (could also be the top-right close button) and the text that was entered will be passed as the two
5440          * parameters to the callback.
5441          * @param {String} title The title bar text
5442          * @param {String} msg The message box body text
5443          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5444          * @param {Object} scope (optional) The scope of the callback function
5445          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5446          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5447          * @return {Roo.MessageBox} This message box
5448          */
5449         prompt : function(title, msg, fn, scope, multiline){
5450             this.show({
5451                 title : title,
5452                 msg : msg,
5453                 buttons: this.OKCANCEL,
5454                 fn: fn,
5455                 minWidth:250,
5456                 scope : scope,
5457                 prompt:true,
5458                 multiline: multiline,
5459                 modal : true
5460             });
5461             return this;
5462         },
5463
5464         /**
5465          * Button config that displays a single OK button
5466          * @type Object
5467          */
5468         OK : {ok:true},
5469         /**
5470          * Button config that displays Yes and No buttons
5471          * @type Object
5472          */
5473         YESNO : {yes:true, no:true},
5474         /**
5475          * Button config that displays OK and Cancel buttons
5476          * @type Object
5477          */
5478         OKCANCEL : {ok:true, cancel:true},
5479         /**
5480          * Button config that displays Yes, No and Cancel buttons
5481          * @type Object
5482          */
5483         YESNOCANCEL : {yes:true, no:true, cancel:true},
5484
5485         /**
5486          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5487          * @type Number
5488          */
5489         defaultTextHeight : 75,
5490         /**
5491          * The maximum width in pixels of the message box (defaults to 600)
5492          * @type Number
5493          */
5494         maxWidth : 600,
5495         /**
5496          * The minimum width in pixels of the message box (defaults to 100)
5497          * @type Number
5498          */
5499         minWidth : 100,
5500         /**
5501          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5502          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5503          * @type Number
5504          */
5505         minProgressWidth : 250,
5506         /**
5507          * An object containing the default button text strings that can be overriden for localized language support.
5508          * Supported properties are: ok, cancel, yes and no.
5509          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5510          * @type Object
5511          */
5512         buttonText : {
5513             ok : "OK",
5514             cancel : "Cancel",
5515             yes : "Yes",
5516             no : "No"
5517         }
5518     };
5519 }();
5520
5521 /**
5522  * Shorthand for {@link Roo.MessageBox}
5523  */
5524 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5525 Roo.Msg = Roo.Msg || Roo.MessageBox;
5526 /*
5527  * - LGPL
5528  *
5529  * navbar
5530  * 
5531  */
5532
5533 /**
5534  * @class Roo.bootstrap.nav.Bar
5535  * @extends Roo.bootstrap.Component
5536  * @abstract
5537  * Bootstrap Navbar class
5538
5539  * @constructor
5540  * Create a new Navbar
5541  * @param {Object} config The config object
5542  */
5543
5544
5545 Roo.bootstrap.nav.Bar = function(config){
5546     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5547     this.addEvents({
5548         // raw events
5549         /**
5550          * @event beforetoggle
5551          * Fire before toggle the menu
5552          * @param {Roo.EventObject} e
5553          */
5554         "beforetoggle" : true
5555     });
5556 };
5557
5558 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5559     
5560     
5561    
5562     // private
5563     navItems : false,
5564     loadMask : false,
5565     
5566     
5567     getAutoCreate : function(){
5568         
5569         
5570         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5571         
5572     },
5573     
5574     initEvents :function ()
5575     {
5576         //Roo.log(this.el.select('.navbar-toggle',true));
5577         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5578         
5579         var mark = {
5580             tag: "div",
5581             cls:"x-dlg-mask"
5582         };
5583         
5584         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5585         
5586         var size = this.el.getSize();
5587         this.maskEl.setSize(size.width, size.height);
5588         this.maskEl.enableDisplayMode("block");
5589         this.maskEl.hide();
5590         
5591         if(this.loadMask){
5592             this.maskEl.show();
5593         }
5594     },
5595     
5596     
5597     getChildContainer : function()
5598     {
5599         if (this.el && this.el.select('.collapse').getCount()) {
5600             return this.el.select('.collapse',true).first();
5601         }
5602         
5603         return this.el;
5604     },
5605     
5606     mask : function()
5607     {
5608         this.maskEl.show();
5609     },
5610     
5611     unmask : function()
5612     {
5613         this.maskEl.hide();
5614     },
5615     onToggle : function()
5616     {
5617         
5618         if(this.fireEvent('beforetoggle', this) === false){
5619             return;
5620         }
5621         var ce = this.el.select('.navbar-collapse',true).first();
5622       
5623         if (!ce.hasClass('show')) {
5624            this.expand();
5625         } else {
5626             this.collapse();
5627         }
5628         
5629         
5630     
5631     },
5632     /**
5633      * Expand the navbar pulldown 
5634      */
5635     expand : function ()
5636     {
5637        
5638         var ce = this.el.select('.navbar-collapse',true).first();
5639         if (ce.hasClass('collapsing')) {
5640             return;
5641         }
5642         ce.dom.style.height = '';
5643                // show it...
5644         ce.addClass('in'); // old...
5645         ce.removeClass('collapse');
5646         ce.addClass('show');
5647         var h = ce.getHeight();
5648         Roo.log(h);
5649         ce.removeClass('show');
5650         // at this point we should be able to see it..
5651         ce.addClass('collapsing');
5652         
5653         ce.setHeight(0); // resize it ...
5654         ce.on('transitionend', function() {
5655             //Roo.log('done transition');
5656             ce.removeClass('collapsing');
5657             ce.addClass('show');
5658             ce.removeClass('collapse');
5659
5660             ce.dom.style.height = '';
5661         }, this, { single: true} );
5662         ce.setHeight(h);
5663         ce.dom.scrollTop = 0;
5664     },
5665     /**
5666      * Collapse the navbar pulldown 
5667      */
5668     collapse : function()
5669     {
5670          var ce = this.el.select('.navbar-collapse',true).first();
5671        
5672         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5673             // it's collapsed or collapsing..
5674             return;
5675         }
5676         ce.removeClass('in'); // old...
5677         ce.setHeight(ce.getHeight());
5678         ce.removeClass('show');
5679         ce.addClass('collapsing');
5680         
5681         ce.on('transitionend', function() {
5682             ce.dom.style.height = '';
5683             ce.removeClass('collapsing');
5684             ce.addClass('collapse');
5685         }, this, { single: true} );
5686         ce.setHeight(0);
5687     }
5688     
5689     
5690     
5691 });
5692
5693
5694
5695  
5696
5697  /*
5698  * - LGPL
5699  *
5700  * navbar
5701  * 
5702  */
5703
5704 /**
5705  * @class Roo.bootstrap.nav.Simplebar
5706  * @extends Roo.bootstrap.nav.Bar
5707  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5708  * Bootstrap Sidebar class
5709  *
5710  * @cfg {Boolean} inverse is inverted color
5711  * 
5712  * @cfg {String} type (nav | pills | tabs)
5713  * @cfg {Boolean} arrangement stacked | justified
5714  * @cfg {String} align (left | right) alignment
5715  * 
5716  * @cfg {Boolean} main (true|false) main nav bar? default false
5717  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5718  * 
5719  * @cfg {String} tag (header|footer|nav|div) default is nav 
5720
5721  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5722  * 
5723  * 
5724  * @constructor
5725  * Create a new Sidebar
5726  * @param {Object} config The config object
5727  */
5728
5729
5730 Roo.bootstrap.nav.Simplebar = function(config){
5731     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5732 };
5733
5734 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5735     
5736     inverse: false,
5737     
5738     type: false,
5739     arrangement: '',
5740     align : false,
5741     
5742     weight : 'light',
5743     
5744     main : false,
5745     
5746     
5747     tag : false,
5748     
5749     
5750     getAutoCreate : function(){
5751         
5752         
5753         var cfg = {
5754             tag : this.tag || 'div',
5755             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5756         };
5757         if (['light','white'].indexOf(this.weight) > -1) {
5758             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5759         }
5760         cfg.cls += ' bg-' + this.weight;
5761         
5762         if (this.inverse) {
5763             cfg.cls += ' navbar-inverse';
5764             
5765         }
5766         
5767         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5768         
5769         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5770             return cfg;
5771         }
5772         
5773         
5774     
5775         
5776         cfg.cn = [
5777             {
5778                 cls: 'nav nav-' + this.xtype,
5779                 tag : 'ul'
5780             }
5781         ];
5782         
5783          
5784         this.type = this.type || 'nav';
5785         if (['tabs','pills'].indexOf(this.type) != -1) {
5786             cfg.cn[0].cls += ' nav-' + this.type
5787         
5788         
5789         } else {
5790             if (this.type!=='nav') {
5791                 Roo.log('nav type must be nav/tabs/pills')
5792             }
5793             cfg.cn[0].cls += ' navbar-nav'
5794         }
5795         
5796         
5797         
5798         
5799         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5800             cfg.cn[0].cls += ' nav-' + this.arrangement;
5801         }
5802         
5803         
5804         if (this.align === 'right') {
5805             cfg.cn[0].cls += ' navbar-right';
5806         }
5807         
5808         
5809         
5810         
5811         return cfg;
5812     
5813         
5814     }
5815     
5816     
5817     
5818 });
5819
5820
5821
5822  
5823
5824  
5825        /*
5826  * - LGPL
5827  *
5828  * navbar
5829  * navbar-fixed-top
5830  * navbar-expand-md  fixed-top 
5831  */
5832
5833 /**
5834  * @class Roo.bootstrap.nav.Headerbar
5835  * @extends Roo.bootstrap.nav.Simplebar
5836  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5837  * Bootstrap Sidebar class
5838  *
5839  * @cfg {String} brand what is brand
5840  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5841  * @cfg {String} brand_href href of the brand
5842  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5843  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5844  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5845  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5846  * 
5847  * @constructor
5848  * Create a new Sidebar
5849  * @param {Object} config The config object
5850  */
5851
5852
5853 Roo.bootstrap.nav.Headerbar = function(config){
5854     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5855       
5856 };
5857
5858 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5859     
5860     position: '',
5861     brand: '',
5862     brand_href: false,
5863     srButton : true,
5864     autohide : false,
5865     desktopCenter : false,
5866    
5867     
5868     getAutoCreate : function(){
5869         
5870         var   cfg = {
5871             tag: this.nav || 'nav',
5872             cls: 'navbar navbar-expand-md',
5873             role: 'navigation',
5874             cn: []
5875         };
5876         
5877         var cn = cfg.cn;
5878         if (this.desktopCenter) {
5879             cn.push({cls : 'container', cn : []});
5880             cn = cn[0].cn;
5881         }
5882         
5883         if(this.srButton){
5884             var btn = {
5885                 tag: 'button',
5886                 type: 'button',
5887                 cls: 'navbar-toggle navbar-toggler',
5888                 'data-toggle': 'collapse',
5889                 cn: [
5890                     {
5891                         tag: 'span',
5892                         cls: 'sr-only',
5893                         html: 'Toggle navigation'
5894                     },
5895                     {
5896                         tag: 'span',
5897                         cls: 'icon-bar navbar-toggler-icon'
5898                     },
5899                     {
5900                         tag: 'span',
5901                         cls: 'icon-bar'
5902                     },
5903                     {
5904                         tag: 'span',
5905                         cls: 'icon-bar'
5906                     }
5907                 ]
5908             };
5909             
5910             cn.push( Roo.bootstrap.version == 4 ? btn : {
5911                 tag: 'div',
5912                 cls: 'navbar-header',
5913                 cn: [
5914                     btn
5915                 ]
5916             });
5917         }
5918         
5919         cn.push({
5920             tag: 'div',
5921             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5922             cn : []
5923         });
5924         
5925         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5926         
5927         if (['light','white'].indexOf(this.weight) > -1) {
5928             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5929         }
5930         cfg.cls += ' bg-' + this.weight;
5931         
5932         
5933         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5934             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5935             
5936             // tag can override this..
5937             
5938             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5939         }
5940         
5941         if (this.brand !== '') {
5942             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5943             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5944                 tag: 'a',
5945                 href: this.brand_href ? this.brand_href : '#',
5946                 cls: 'navbar-brand',
5947                 cn: [
5948                 this.brand
5949                 ]
5950             });
5951         }
5952         
5953         if(this.main){
5954             cfg.cls += ' main-nav';
5955         }
5956         
5957         
5958         return cfg;
5959
5960         
5961     },
5962     getHeaderChildContainer : function()
5963     {
5964         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5965             return this.el.select('.navbar-header',true).first();
5966         }
5967         
5968         return this.getChildContainer();
5969     },
5970     
5971     getChildContainer : function()
5972     {
5973          
5974         return this.el.select('.roo-navbar-collapse',true).first();
5975          
5976         
5977     },
5978     
5979     initEvents : function()
5980     {
5981         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5982         
5983         if (this.autohide) {
5984             
5985             var prevScroll = 0;
5986             var ft = this.el;
5987             
5988             Roo.get(document).on('scroll',function(e) {
5989                 var ns = Roo.get(document).getScroll().top;
5990                 var os = prevScroll;
5991                 prevScroll = ns;
5992                 
5993                 if(ns > os){
5994                     ft.removeClass('slideDown');
5995                     ft.addClass('slideUp');
5996                     return;
5997                 }
5998                 ft.removeClass('slideUp');
5999                 ft.addClass('slideDown');
6000                  
6001               
6002           },this);
6003         }
6004     }    
6005     
6006 });
6007
6008
6009
6010  
6011
6012  /*
6013  * - LGPL
6014  *
6015  * navbar
6016  * 
6017  */
6018
6019 /**
6020  * @class Roo.bootstrap.nav.Sidebar
6021  * @extends Roo.bootstrap.nav.Bar
6022  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6023  * Bootstrap Sidebar class
6024  * 
6025  * @constructor
6026  * Create a new Sidebar
6027  * @param {Object} config The config object
6028  */
6029
6030
6031 Roo.bootstrap.nav.Sidebar = function(config){
6032     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6033 };
6034
6035 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6036     
6037     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6038     
6039     getAutoCreate : function(){
6040         
6041         
6042         return  {
6043             tag: 'div',
6044             cls: 'sidebar sidebar-nav'
6045         };
6046     
6047         
6048     }
6049     
6050     
6051     
6052 });
6053
6054
6055
6056  
6057
6058  /*
6059  * - LGPL
6060  *
6061  * nav group
6062  * 
6063  */
6064
6065 /**
6066  * @class Roo.bootstrap.nav.Group
6067  * @extends Roo.bootstrap.Component
6068  * @children Roo.bootstrap.nav.Item
6069  * Bootstrap NavGroup class
6070  * @cfg {String} align (left|right)
6071  * @cfg {Boolean} inverse
6072  * @cfg {String} type (nav|pills|tab) default nav
6073  * @cfg {String} navId - reference Id for navbar.
6074  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6075  * 
6076  * @constructor
6077  * Create a new nav group
6078  * @param {Object} config The config object
6079  */
6080
6081 Roo.bootstrap.nav.Group = function(config){
6082     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6083     this.navItems = [];
6084    
6085     Roo.bootstrap.nav.Group.register(this);
6086      this.addEvents({
6087         /**
6088              * @event changed
6089              * Fires when the active item changes
6090              * @param {Roo.bootstrap.nav.Group} this
6091              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6092              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6093          */
6094         'changed': true
6095      });
6096     
6097 };
6098
6099 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6100     
6101     align: '',
6102     inverse: false,
6103     form: false,
6104     type: 'nav',
6105     navId : '',
6106     // private
6107     pilltype : true,
6108     
6109     navItems : false, 
6110     
6111     getAutoCreate : function()
6112     {
6113         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6114         
6115         cfg = {
6116             tag : 'ul',
6117             cls: 'nav' 
6118         };
6119         if (Roo.bootstrap.version == 4) {
6120             if (['tabs','pills'].indexOf(this.type) != -1) {
6121                 cfg.cls += ' nav-' + this.type; 
6122             } else {
6123                 // trying to remove so header bar can right align top?
6124                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6125                     // do not use on header bar... 
6126                     cfg.cls += ' navbar-nav';
6127                 }
6128             }
6129             
6130         } else {
6131             if (['tabs','pills'].indexOf(this.type) != -1) {
6132                 cfg.cls += ' nav-' + this.type
6133             } else {
6134                 if (this.type !== 'nav') {
6135                     Roo.log('nav type must be nav/tabs/pills')
6136                 }
6137                 cfg.cls += ' navbar-nav'
6138             }
6139         }
6140         
6141         if (this.parent() && this.parent().sidebar) {
6142             cfg = {
6143                 tag: 'ul',
6144                 cls: 'dashboard-menu sidebar-menu'
6145             };
6146             
6147             return cfg;
6148         }
6149         
6150         if (this.form === true) {
6151             cfg = {
6152                 tag: 'form',
6153                 cls: 'navbar-form form-inline'
6154             };
6155             //nav navbar-right ml-md-auto
6156             if (this.align === 'right') {
6157                 cfg.cls += ' navbar-right ml-md-auto';
6158             } else {
6159                 cfg.cls += ' navbar-left';
6160             }
6161         }
6162         
6163         if (this.align === 'right') {
6164             cfg.cls += ' navbar-right ml-md-auto';
6165         } else {
6166             cfg.cls += ' mr-auto';
6167         }
6168         
6169         if (this.inverse) {
6170             cfg.cls += ' navbar-inverse';
6171             
6172         }
6173         
6174         
6175         return cfg;
6176     },
6177     /**
6178     * sets the active Navigation item
6179     * @param {Roo.bootstrap.nav.Item} the new current navitem
6180     */
6181     setActiveItem : function(item)
6182     {
6183         var prev = false;
6184         Roo.each(this.navItems, function(v){
6185             if (v == item) {
6186                 return ;
6187             }
6188             if (v.isActive()) {
6189                 v.setActive(false, true);
6190                 prev = v;
6191                 
6192             }
6193             
6194         });
6195
6196         item.setActive(true, true);
6197         this.fireEvent('changed', this, item, prev);
6198         
6199         
6200     },
6201     /**
6202     * gets the active Navigation item
6203     * @return {Roo.bootstrap.nav.Item} the current navitem
6204     */
6205     getActive : function()
6206     {
6207         
6208         var prev = false;
6209         Roo.each(this.navItems, function(v){
6210             
6211             if (v.isActive()) {
6212                 prev = v;
6213                 
6214             }
6215             
6216         });
6217         return prev;
6218     },
6219     
6220     indexOfNav : function()
6221     {
6222         
6223         var prev = false;
6224         Roo.each(this.navItems, function(v,i){
6225             
6226             if (v.isActive()) {
6227                 prev = i;
6228                 
6229             }
6230             
6231         });
6232         return prev;
6233     },
6234     /**
6235     * adds a Navigation item
6236     * @param {Roo.bootstrap.nav.Item} the navitem to add
6237     */
6238     addItem : function(cfg)
6239     {
6240         if (this.form && Roo.bootstrap.version == 4) {
6241             cfg.tag = 'div';
6242         }
6243         var cn = new Roo.bootstrap.nav.Item(cfg);
6244         this.register(cn);
6245         cn.parentId = this.id;
6246         cn.onRender(this.el, null);
6247         return cn;
6248     },
6249     /**
6250     * register a Navigation item
6251     * @param {Roo.bootstrap.nav.Item} the navitem to add
6252     */
6253     register : function(item)
6254     {
6255         this.navItems.push( item);
6256         item.navId = this.navId;
6257     
6258     },
6259     
6260     /**
6261     * clear all the Navigation item
6262     */
6263    
6264     clearAll : function()
6265     {
6266         this.navItems = [];
6267         this.el.dom.innerHTML = '';
6268     },
6269     
6270     getNavItem: function(tabId)
6271     {
6272         var ret = false;
6273         Roo.each(this.navItems, function(e) {
6274             if (e.tabId == tabId) {
6275                ret =  e;
6276                return false;
6277             }
6278             return true;
6279             
6280         });
6281         return ret;
6282     },
6283     
6284     setActiveNext : function()
6285     {
6286         var i = this.indexOfNav(this.getActive());
6287         if (i > this.navItems.length) {
6288             return;
6289         }
6290         this.setActiveItem(this.navItems[i+1]);
6291     },
6292     setActivePrev : function()
6293     {
6294         var i = this.indexOfNav(this.getActive());
6295         if (i  < 1) {
6296             return;
6297         }
6298         this.setActiveItem(this.navItems[i-1]);
6299     },
6300     clearWasActive : function(except) {
6301         Roo.each(this.navItems, function(e) {
6302             if (e.tabId != except.tabId && e.was_active) {
6303                e.was_active = false;
6304                return false;
6305             }
6306             return true;
6307             
6308         });
6309     },
6310     getWasActive : function ()
6311     {
6312         var r = false;
6313         Roo.each(this.navItems, function(e) {
6314             if (e.was_active) {
6315                r = e;
6316                return false;
6317             }
6318             return true;
6319             
6320         });
6321         return r;
6322     }
6323     
6324     
6325 });
6326
6327  
6328 Roo.apply(Roo.bootstrap.nav.Group, {
6329     
6330     groups: {},
6331      /**
6332     * register a Navigation Group
6333     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6334     */
6335     register : function(navgrp)
6336     {
6337         this.groups[navgrp.navId] = navgrp;
6338         
6339     },
6340     /**
6341     * fetch a Navigation Group based on the navigation ID
6342     * @param {string} the navgroup to add
6343     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6344     */
6345     get: function(navId) {
6346         if (typeof(this.groups[navId]) == 'undefined') {
6347             return false;
6348             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6349         }
6350         return this.groups[navId] ;
6351     }
6352     
6353     
6354     
6355 });
6356
6357  /**
6358  * @class Roo.bootstrap.nav.Item
6359  * @extends Roo.bootstrap.Component
6360  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6361  * @parent Roo.bootstrap.nav.Group
6362  * @licence LGPL
6363  * Bootstrap Navbar.NavItem class
6364  * 
6365  * @cfg {String} href  link to
6366  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6367  * @cfg {Boolean} button_outline show and outlined button
6368  * @cfg {String} html content of button
6369  * @cfg {String} badge text inside badge
6370  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6371  * @cfg {String} glyphicon DEPRICATED - use fa
6372  * @cfg {String} icon DEPRICATED - use fa
6373  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6374  * @cfg {Boolean} active Is item active
6375  * @cfg {Boolean} disabled Is item disabled
6376  * @cfg {String} linkcls  Link Class
6377  * @cfg {Boolean} preventDefault (true | false) default false
6378  * @cfg {String} tabId the tab that this item activates.
6379  * @cfg {String} tagtype (a|span) render as a href or span?
6380  * @cfg {Boolean} animateRef (true|false) link to element default false  
6381  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6382   
6383  * @constructor
6384  * Create a new Navbar Item
6385  * @param {Object} config The config object
6386  */
6387 Roo.bootstrap.nav.Item = function(config){
6388     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6389     this.addEvents({
6390         // raw events
6391         /**
6392          * @event click
6393          * The raw click event for the entire grid.
6394          * @param {Roo.EventObject} e
6395          */
6396         "click" : true,
6397          /**
6398             * @event changed
6399             * Fires when the active item active state changes
6400             * @param {Roo.bootstrap.nav.Item} this
6401             * @param {boolean} state the new state
6402              
6403          */
6404         'changed': true,
6405         /**
6406             * @event scrollto
6407             * Fires when scroll to element
6408             * @param {Roo.bootstrap.nav.Item} this
6409             * @param {Object} options
6410             * @param {Roo.EventObject} e
6411              
6412          */
6413         'scrollto': true
6414     });
6415    
6416 };
6417
6418 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6419     
6420     href: false,
6421     html: '',
6422     badge: '',
6423     icon: false,
6424     fa : false,
6425     glyphicon: false,
6426     active: false,
6427     preventDefault : false,
6428     tabId : false,
6429     tagtype : 'a',
6430     tag: 'li',
6431     disabled : false,
6432     animateRef : false,
6433     was_active : false,
6434     button_weight : '',
6435     button_outline : false,
6436     linkcls : '',
6437     navLink: false,
6438     
6439     getAutoCreate : function(){
6440          
6441         var cfg = {
6442             tag: this.tag,
6443             cls: 'nav-item'
6444         };
6445         
6446         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6447         
6448         if (this.active) {
6449             cfg.cls +=  ' active' ;
6450         }
6451         if (this.disabled) {
6452             cfg.cls += ' disabled';
6453         }
6454         
6455         // BS4 only?
6456         if (this.button_weight.length) {
6457             cfg.tag = this.href ? 'a' : 'button';
6458             cfg.html = this.html || '';
6459             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6460             if (this.href) {
6461                 cfg.href = this.href;
6462             }
6463             if (this.fa) {
6464                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6465             } else {
6466                 cfg.cls += " nav-html";
6467             }
6468             
6469             // menu .. should add dropdown-menu class - so no need for carat..
6470             
6471             if (this.badge !== '') {
6472                  
6473                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6474             }
6475             return cfg;
6476         }
6477         
6478         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6479             cfg.cn = [
6480                 {
6481                     tag: this.tagtype,
6482                     href : this.href || "#",
6483                     html: this.html || '',
6484                     cls : ''
6485                 }
6486             ];
6487             if (this.tagtype == 'a') {
6488                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6489         
6490             }
6491             if (this.icon) {
6492                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6493             } else  if (this.fa) {
6494                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6495             } else if(this.glyphicon) {
6496                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6497             } else {
6498                 cfg.cn[0].cls += " nav-html";
6499             }
6500             
6501             if (this.menu) {
6502                 cfg.cn[0].html += " <span class='caret'></span>";
6503              
6504             }
6505             
6506             if (this.badge !== '') {
6507                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6508             }
6509         }
6510         
6511         
6512         
6513         return cfg;
6514     },
6515     onRender : function(ct, position)
6516     {
6517        // Roo.log("Call onRender: " + this.xtype);
6518         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6519             this.tag = 'div';
6520         }
6521         
6522         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6523         this.navLink = this.el.select('.nav-link',true).first();
6524         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6525         return ret;
6526     },
6527       
6528     
6529     initEvents: function() 
6530     {
6531         if (typeof (this.menu) != 'undefined') {
6532             this.menu.parentType = this.xtype;
6533             this.menu.triggerEl = this.el;
6534             this.menu = this.addxtype(Roo.apply({}, this.menu));
6535         }
6536         
6537         this.el.on('click', this.onClick, this);
6538         
6539         //if(this.tagtype == 'span'){
6540         //    this.el.select('span',true).on('click', this.onClick, this);
6541         //}
6542        
6543         // at this point parent should be available..
6544         this.parent().register(this);
6545     },
6546     
6547     onClick : function(e)
6548     {
6549         if (e.getTarget('.dropdown-menu-item')) {
6550             // did you click on a menu itemm.... - then don't trigger onclick..
6551             return;
6552         }
6553         
6554         if(
6555                 this.preventDefault ||
6556                                 this.href === false ||
6557                 this.href === '#' 
6558         ){
6559             //Roo.log("NavItem - prevent Default?");
6560             e.preventDefault();
6561         }
6562         
6563         if (this.disabled) {
6564             return;
6565         }
6566         
6567         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6568         if (tg && tg.transition) {
6569             Roo.log("waiting for the transitionend");
6570             return;
6571         }
6572         
6573         
6574         
6575         //Roo.log("fire event clicked");
6576         if(this.fireEvent('click', this, e) === false){
6577             return;
6578         };
6579         
6580         if(this.tagtype == 'span'){
6581             return;
6582         }
6583         
6584         //Roo.log(this.href);
6585         var ael = this.el.select('a',true).first();
6586         //Roo.log(ael);
6587         
6588         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6589             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6590             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6591                 return; // ignore... - it's a 'hash' to another page.
6592             }
6593             Roo.log("NavItem - prevent Default?");
6594             e.preventDefault();
6595             this.scrollToElement(e);
6596         }
6597         
6598         
6599         var p =  this.parent();
6600    
6601         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6602             if (typeof(p.setActiveItem) !== 'undefined') {
6603                 p.setActiveItem(this);
6604             }
6605         }
6606         
6607         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6608         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6609             // remove the collapsed menu expand...
6610             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6611         }
6612     },
6613     
6614     isActive: function () {
6615         return this.active
6616     },
6617     setActive : function(state, fire, is_was_active)
6618     {
6619         if (this.active && !state && this.navId) {
6620             this.was_active = true;
6621             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6622             if (nv) {
6623                 nv.clearWasActive(this);
6624             }
6625             
6626         }
6627         this.active = state;
6628         
6629         if (!state ) {
6630             this.el.removeClass('active');
6631             this.navLink ? this.navLink.removeClass('active') : false;
6632         } else if (!this.el.hasClass('active')) {
6633             
6634             this.el.addClass('active');
6635             if (Roo.bootstrap.version == 4 && this.navLink ) {
6636                 this.navLink.addClass('active');
6637             }
6638             
6639         }
6640         if (fire) {
6641             this.fireEvent('changed', this, state);
6642         }
6643         
6644         // show a panel if it's registered and related..
6645         
6646         if (!this.navId || !this.tabId || !state || is_was_active) {
6647             return;
6648         }
6649         
6650         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6651         if (!tg) {
6652             return;
6653         }
6654         var pan = tg.getPanelByName(this.tabId);
6655         if (!pan) {
6656             return;
6657         }
6658         // if we can not flip to new panel - go back to old nav highlight..
6659         if (false == tg.showPanel(pan)) {
6660             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6661             if (nv) {
6662                 var onav = nv.getWasActive();
6663                 if (onav) {
6664                     onav.setActive(true, false, true);
6665                 }
6666             }
6667             
6668         }
6669         
6670         
6671         
6672     },
6673      // this should not be here...
6674     setDisabled : function(state)
6675     {
6676         this.disabled = state;
6677         if (!state ) {
6678             this.el.removeClass('disabled');
6679         } else if (!this.el.hasClass('disabled')) {
6680             this.el.addClass('disabled');
6681         }
6682         
6683     },
6684     
6685     /**
6686      * Fetch the element to display the tooltip on.
6687      * @return {Roo.Element} defaults to this.el
6688      */
6689     tooltipEl : function()
6690     {
6691         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6692     },
6693     
6694     scrollToElement : function(e)
6695     {
6696         var c = document.body;
6697         
6698         /*
6699          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6700          */
6701         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6702             c = document.documentElement;
6703         }
6704         
6705         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6706         
6707         if(!target){
6708             return;
6709         }
6710
6711         var o = target.calcOffsetsTo(c);
6712         
6713         var options = {
6714             target : target,
6715             value : o[1]
6716         };
6717         
6718         this.fireEvent('scrollto', this, options, e);
6719         
6720         Roo.get(c).scrollTo('top', options.value, true);
6721         
6722         return;
6723     },
6724     /**
6725      * Set the HTML (text content) of the item
6726      * @param {string} html  content for the nav item
6727      */
6728     setHtml : function(html)
6729     {
6730         this.html = html;
6731         this.htmlEl.dom.innerHTML = html;
6732         
6733     } 
6734 });
6735  
6736
6737  /*
6738  * - LGPL
6739  *
6740  * sidebar item
6741  *
6742  *  li
6743  *    <span> icon </span>
6744  *    <span> text </span>
6745  *    <span>badge </span>
6746  */
6747
6748 /**
6749  * @class Roo.bootstrap.nav.SidebarItem
6750  * @extends Roo.bootstrap.nav.Item
6751  * Bootstrap Navbar.NavSidebarItem class
6752  * 
6753  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6754  * {Boolean} open is the menu open
6755  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6756  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6757  * {String} buttonSize (sm|md|lg)the extra classes for the button
6758  * {Boolean} showArrow show arrow next to the text (default true)
6759  * @constructor
6760  * Create a new Navbar Button
6761  * @param {Object} config The config object
6762  */
6763 Roo.bootstrap.nav.SidebarItem = function(config){
6764     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6765     this.addEvents({
6766         // raw events
6767         /**
6768          * @event click
6769          * The raw click event for the entire grid.
6770          * @param {Roo.EventObject} e
6771          */
6772         "click" : true,
6773          /**
6774             * @event changed
6775             * Fires when the active item active state changes
6776             * @param {Roo.bootstrap.nav.SidebarItem} this
6777             * @param {boolean} state the new state
6778              
6779          */
6780         'changed': true
6781     });
6782    
6783 };
6784
6785 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6786     
6787     badgeWeight : 'default',
6788     
6789     open: false,
6790     
6791     buttonView : false,
6792     
6793     buttonWeight : 'default',
6794     
6795     buttonSize : 'md',
6796     
6797     showArrow : true,
6798     
6799     getAutoCreate : function(){
6800         
6801         
6802         var a = {
6803                 tag: 'a',
6804                 href : this.href || '#',
6805                 cls: '',
6806                 html : '',
6807                 cn : []
6808         };
6809         
6810         if(this.buttonView){
6811             a = {
6812                 tag: 'button',
6813                 href : this.href || '#',
6814                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6815                 html : this.html,
6816                 cn : []
6817             };
6818         }
6819         
6820         var cfg = {
6821             tag: 'li',
6822             cls: '',
6823             cn: [ a ]
6824         };
6825         
6826         if (this.active) {
6827             cfg.cls += ' active';
6828         }
6829         
6830         if (this.disabled) {
6831             cfg.cls += ' disabled';
6832         }
6833         if (this.open) {
6834             cfg.cls += ' open x-open';
6835         }
6836         // left icon..
6837         if (this.glyphicon || this.icon) {
6838             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6839             a.cn.push({ tag : 'i', cls : c }) ;
6840         }
6841         
6842         if(!this.buttonView){
6843             var span = {
6844                 tag: 'span',
6845                 html : this.html || ''
6846             };
6847
6848             a.cn.push(span);
6849             
6850         }
6851         
6852         if (this.badge !== '') {
6853             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6854         }
6855         
6856         if (this.menu) {
6857             
6858             if(this.showArrow){
6859                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6860             }
6861             
6862             a.cls += ' dropdown-toggle treeview' ;
6863         }
6864         
6865         return cfg;
6866     },
6867     
6868     initEvents : function()
6869     { 
6870         if (typeof (this.menu) != 'undefined') {
6871             this.menu.parentType = this.xtype;
6872             this.menu.triggerEl = this.el;
6873             this.menu = this.addxtype(Roo.apply({}, this.menu));
6874         }
6875         
6876         this.el.on('click', this.onClick, this);
6877         
6878         if(this.badge !== ''){
6879             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6880         }
6881         
6882     },
6883     
6884     onClick : function(e)
6885     {
6886         if(this.disabled){
6887             e.preventDefault();
6888             return;
6889         }
6890         
6891         if(this.preventDefault){
6892             e.preventDefault();
6893         }
6894         
6895         this.fireEvent('click', this, e);
6896     },
6897     
6898     disable : function()
6899     {
6900         this.setDisabled(true);
6901     },
6902     
6903     enable : function()
6904     {
6905         this.setDisabled(false);
6906     },
6907     
6908     setDisabled : function(state)
6909     {
6910         if(this.disabled == state){
6911             return;
6912         }
6913         
6914         this.disabled = state;
6915         
6916         if (state) {
6917             this.el.addClass('disabled');
6918             return;
6919         }
6920         
6921         this.el.removeClass('disabled');
6922         
6923         return;
6924     },
6925     
6926     setActive : function(state)
6927     {
6928         if(this.active == state){
6929             return;
6930         }
6931         
6932         this.active = state;
6933         
6934         if (state) {
6935             this.el.addClass('active');
6936             return;
6937         }
6938         
6939         this.el.removeClass('active');
6940         
6941         return;
6942     },
6943     
6944     isActive: function () 
6945     {
6946         return this.active;
6947     },
6948     
6949     setBadge : function(str)
6950     {
6951         if(!this.badgeEl){
6952             return;
6953         }
6954         
6955         this.badgeEl.dom.innerHTML = str;
6956     }
6957     
6958    
6959      
6960  
6961 });
6962  
6963
6964  /*
6965  * - LGPL
6966  *
6967  * nav progress bar
6968  * 
6969  */
6970
6971 /**
6972  * @class Roo.bootstrap.nav.ProgressBar
6973  * @extends Roo.bootstrap.Component
6974  * @children Roo.bootstrap.nav.ProgressBarItem
6975  * Bootstrap NavProgressBar class
6976  * 
6977  * @constructor
6978  * Create a new nav progress bar - a bar indicating step along a process
6979  * @param {Object} config The config object
6980  */
6981
6982 Roo.bootstrap.nav.ProgressBar = function(config){
6983     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6984
6985     this.bullets = this.bullets || [];
6986    
6987 //    Roo.bootstrap.nav.ProgressBar.register(this);
6988      this.addEvents({
6989         /**
6990              * @event changed
6991              * Fires when the active item changes
6992              * @param {Roo.bootstrap.nav.ProgressBar} this
6993              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6994              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
6995          */
6996         'changed': true
6997      });
6998     
6999 };
7000
7001 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
7002     /**
7003      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7004      * Bullets for the Nav Progress bar for the toolbar
7005      */
7006     bullets : [],
7007     barItems : [],
7008     
7009     getAutoCreate : function()
7010     {
7011         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7012         
7013         cfg = {
7014             tag : 'div',
7015             cls : 'roo-navigation-bar-group',
7016             cn : [
7017                 {
7018                     tag : 'div',
7019                     cls : 'roo-navigation-top-bar'
7020                 },
7021                 {
7022                     tag : 'div',
7023                     cls : 'roo-navigation-bullets-bar',
7024                     cn : [
7025                         {
7026                             tag : 'ul',
7027                             cls : 'roo-navigation-bar'
7028                         }
7029                     ]
7030                 },
7031                 
7032                 {
7033                     tag : 'div',
7034                     cls : 'roo-navigation-bottom-bar'
7035                 }
7036             ]
7037             
7038         };
7039         
7040         return cfg;
7041         
7042     },
7043     
7044     initEvents: function() 
7045     {
7046         
7047     },
7048     
7049     onRender : function(ct, position) 
7050     {
7051         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7052         
7053         if(this.bullets.length){
7054             Roo.each(this.bullets, function(b){
7055                this.addItem(b);
7056             }, this);
7057         }
7058         
7059         this.format();
7060         
7061     },
7062     
7063     addItem : function(cfg)
7064     {
7065         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7066         
7067         item.parentId = this.id;
7068         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7069         
7070         if(cfg.html){
7071             var top = new Roo.bootstrap.Element({
7072                 tag : 'div',
7073                 cls : 'roo-navigation-bar-text'
7074             });
7075             
7076             var bottom = new Roo.bootstrap.Element({
7077                 tag : 'div',
7078                 cls : 'roo-navigation-bar-text'
7079             });
7080             
7081             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7082             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7083             
7084             var topText = new Roo.bootstrap.Element({
7085                 tag : 'span',
7086                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7087             });
7088             
7089             var bottomText = new Roo.bootstrap.Element({
7090                 tag : 'span',
7091                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7092             });
7093             
7094             topText.onRender(top.el, null);
7095             bottomText.onRender(bottom.el, null);
7096             
7097             item.topEl = top;
7098             item.bottomEl = bottom;
7099         }
7100         
7101         this.barItems.push(item);
7102         
7103         return item;
7104     },
7105     
7106     getActive : function()
7107     {
7108         var active = false;
7109         
7110         Roo.each(this.barItems, function(v){
7111             
7112             if (!v.isActive()) {
7113                 return;
7114             }
7115             
7116             active = v;
7117             return false;
7118             
7119         });
7120         
7121         return active;
7122     },
7123     
7124     setActiveItem : function(item)
7125     {
7126         var prev = false;
7127         
7128         Roo.each(this.barItems, function(v){
7129             if (v.rid == item.rid) {
7130                 return ;
7131             }
7132             
7133             if (v.isActive()) {
7134                 v.setActive(false);
7135                 prev = v;
7136             }
7137         });
7138
7139         item.setActive(true);
7140         
7141         this.fireEvent('changed', this, item, prev);
7142     },
7143     
7144     getBarItem: function(rid)
7145     {
7146         var ret = false;
7147         
7148         Roo.each(this.barItems, function(e) {
7149             if (e.rid != rid) {
7150                 return;
7151             }
7152             
7153             ret =  e;
7154             return false;
7155         });
7156         
7157         return ret;
7158     },
7159     
7160     indexOfItem : function(item)
7161     {
7162         var index = false;
7163         
7164         Roo.each(this.barItems, function(v, i){
7165             
7166             if (v.rid != item.rid) {
7167                 return;
7168             }
7169             
7170             index = i;
7171             return false
7172         });
7173         
7174         return index;
7175     },
7176     
7177     setActiveNext : function()
7178     {
7179         var i = this.indexOfItem(this.getActive());
7180         
7181         if (i > this.barItems.length) {
7182             return;
7183         }
7184         
7185         this.setActiveItem(this.barItems[i+1]);
7186     },
7187     
7188     setActivePrev : function()
7189     {
7190         var i = this.indexOfItem(this.getActive());
7191         
7192         if (i  < 1) {
7193             return;
7194         }
7195         
7196         this.setActiveItem(this.barItems[i-1]);
7197     },
7198     
7199     format : function()
7200     {
7201         if(!this.barItems.length){
7202             return;
7203         }
7204      
7205         var width = 100 / this.barItems.length;
7206         
7207         Roo.each(this.barItems, function(i){
7208             i.el.setStyle('width', width + '%');
7209             i.topEl.el.setStyle('width', width + '%');
7210             i.bottomEl.el.setStyle('width', width + '%');
7211         }, this);
7212         
7213     }
7214     
7215 });
7216 /*
7217  * - LGPL
7218  *
7219  * Nav Progress Item
7220  * 
7221  */
7222
7223 /**
7224  * @class Roo.bootstrap.nav.ProgressBarItem
7225  * @extends Roo.bootstrap.Component
7226  * Bootstrap NavProgressBarItem class
7227  * @cfg {String} rid the reference id
7228  * @cfg {Boolean} active (true|false) Is item active default false
7229  * @cfg {Boolean} disabled (true|false) Is item active default false
7230  * @cfg {String} html
7231  * @cfg {String} position (top|bottom) text position default bottom
7232  * @cfg {String} icon show icon instead of number
7233  * 
7234  * @constructor
7235  * Create a new NavProgressBarItem
7236  * @param {Object} config The config object
7237  */
7238 Roo.bootstrap.nav.ProgressBarItem = function(config){
7239     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7240     this.addEvents({
7241         // raw events
7242         /**
7243          * @event click
7244          * The raw click event for the entire grid.
7245          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7246          * @param {Roo.EventObject} e
7247          */
7248         "click" : true
7249     });
7250    
7251 };
7252
7253 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7254     
7255     rid : '',
7256     active : false,
7257     disabled : false,
7258     html : '',
7259     position : 'bottom',
7260     icon : false,
7261     
7262     getAutoCreate : function()
7263     {
7264         var iconCls = 'roo-navigation-bar-item-icon';
7265         
7266         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7267         
7268         var cfg = {
7269             tag: 'li',
7270             cls: 'roo-navigation-bar-item',
7271             cn : [
7272                 {
7273                     tag : 'i',
7274                     cls : iconCls
7275                 }
7276             ]
7277         };
7278         
7279         if(this.active){
7280             cfg.cls += ' active';
7281         }
7282         if(this.disabled){
7283             cfg.cls += ' disabled';
7284         }
7285         
7286         return cfg;
7287     },
7288     
7289     disable : function()
7290     {
7291         this.setDisabled(true);
7292     },
7293     
7294     enable : function()
7295     {
7296         this.setDisabled(false);
7297     },
7298     
7299     initEvents: function() 
7300     {
7301         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7302         
7303         this.iconEl.on('click', this.onClick, this);
7304     },
7305     
7306     onClick : function(e)
7307     {
7308         e.preventDefault();
7309         
7310         if(this.disabled){
7311             return;
7312         }
7313         
7314         if(this.fireEvent('click', this, e) === false){
7315             return;
7316         };
7317         
7318         this.parent().setActiveItem(this);
7319     },
7320     
7321     isActive: function () 
7322     {
7323         return this.active;
7324     },
7325     
7326     setActive : function(state)
7327     {
7328         if(this.active == state){
7329             return;
7330         }
7331         
7332         this.active = state;
7333         
7334         if (state) {
7335             this.el.addClass('active');
7336             return;
7337         }
7338         
7339         this.el.removeClass('active');
7340         
7341         return;
7342     },
7343     
7344     setDisabled : function(state)
7345     {
7346         if(this.disabled == state){
7347             return;
7348         }
7349         
7350         this.disabled = state;
7351         
7352         if (state) {
7353             this.el.addClass('disabled');
7354             return;
7355         }
7356         
7357         this.el.removeClass('disabled');
7358     },
7359     
7360     tooltipEl : function()
7361     {
7362         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7363     }
7364 });
7365  
7366
7367  /*
7368  * - LGPL
7369  *
7370  *  Breadcrumb Nav
7371  * 
7372  */
7373 Roo.namespace('Roo.bootstrap.breadcrumb');
7374
7375
7376 /**
7377  * @class Roo.bootstrap.breadcrumb.Nav
7378  * @extends Roo.bootstrap.Component
7379  * Bootstrap Breadcrumb Nav Class
7380  *  
7381  * @children Roo.bootstrap.breadcrumb.Item
7382  * 
7383  * @constructor
7384  * Create a new breadcrumb.Nav
7385  * @param {Object} config The config object
7386  */
7387
7388
7389 Roo.bootstrap.breadcrumb.Nav = function(config){
7390     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7391     
7392     
7393 };
7394
7395 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7396     
7397     getAutoCreate : function()
7398     {
7399
7400         var cfg = {
7401             tag: 'nav',
7402             cn : [
7403                 {
7404                     tag : 'ol',
7405                     cls : 'breadcrumb'
7406                 }
7407             ]
7408             
7409         };
7410           
7411         return cfg;
7412     },
7413     
7414     initEvents: function()
7415     {
7416         this.olEl = this.el.select('ol',true).first();    
7417     },
7418     getChildContainer : function()
7419     {
7420         return this.olEl;  
7421     }
7422     
7423 });
7424
7425  /*
7426  * - LGPL
7427  *
7428  *  Breadcrumb Item
7429  * 
7430  */
7431
7432
7433 /**
7434  * @class Roo.bootstrap.breadcrumb.Nav
7435  * @extends Roo.bootstrap.Component
7436  * @children Roo.bootstrap.Component
7437  * @parent Roo.bootstrap.breadcrumb.Nav
7438  * Bootstrap Breadcrumb Nav Class
7439  *  
7440  * 
7441  * @cfg {String} html the content of the link.
7442  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7443  * @cfg {Boolean} active is it active
7444
7445  * 
7446  * @constructor
7447  * Create a new breadcrumb.Nav
7448  * @param {Object} config The config object
7449  */
7450
7451 Roo.bootstrap.breadcrumb.Item = function(config){
7452     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7453     this.addEvents({
7454         // img events
7455         /**
7456          * @event click
7457          * The img click event for the img.
7458          * @param {Roo.EventObject} e
7459          */
7460         "click" : true
7461     });
7462     
7463 };
7464
7465 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7466     
7467     href: false,
7468     html : '',
7469     
7470     getAutoCreate : function()
7471     {
7472
7473         var cfg = {
7474             tag: 'li',
7475             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7476         };
7477         if (this.href !== false) {
7478             cfg.cn = [{
7479                 tag : 'a',
7480                 href : this.href,
7481                 html : this.html
7482             }];
7483         } else {
7484             cfg.html = this.html;
7485         }
7486         
7487         return cfg;
7488     },
7489     
7490     initEvents: function()
7491     {
7492         if (this.href) {
7493             this.el.select('a', true).first().on('click',this.onClick, this)
7494         }
7495         
7496     },
7497     onClick : function(e)
7498     {
7499         e.preventDefault();
7500         this.fireEvent('click',this,  e);
7501     }
7502     
7503 });
7504
7505  /*
7506  * - LGPL
7507  *
7508  * row
7509  * 
7510  */
7511
7512 /**
7513  * @class Roo.bootstrap.Row
7514  * @extends Roo.bootstrap.Component
7515  * @children Roo.bootstrap.Component
7516  * Bootstrap Row class (contains columns...)
7517  * 
7518  * @constructor
7519  * Create a new Row
7520  * @param {Object} config The config object
7521  */
7522
7523 Roo.bootstrap.Row = function(config){
7524     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7525 };
7526
7527 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7528     
7529     getAutoCreate : function(){
7530        return {
7531             cls: 'row clearfix'
7532        };
7533     }
7534     
7535     
7536 });
7537
7538  
7539
7540  /*
7541  * - LGPL
7542  *
7543  * pagination
7544  * 
7545  */
7546
7547 /**
7548  * @class Roo.bootstrap.Pagination
7549  * @extends Roo.bootstrap.Component
7550  * @children Roo.bootstrap.Pagination
7551  * Bootstrap Pagination class
7552  * 
7553  * @cfg {String} size (xs|sm|md|lg|xl)
7554  * @cfg {Boolean} inverse 
7555  * 
7556  * @constructor
7557  * Create a new Pagination
7558  * @param {Object} config The config object
7559  */
7560
7561 Roo.bootstrap.Pagination = function(config){
7562     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7563 };
7564
7565 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7566     
7567     cls: false,
7568     size: false,
7569     inverse: false,
7570     
7571     getAutoCreate : function(){
7572         var cfg = {
7573             tag: 'ul',
7574                 cls: 'pagination'
7575         };
7576         if (this.inverse) {
7577             cfg.cls += ' inverse';
7578         }
7579         if (this.html) {
7580             cfg.html=this.html;
7581         }
7582         if (this.cls) {
7583             cfg.cls += " " + this.cls;
7584         }
7585         return cfg;
7586     }
7587    
7588 });
7589
7590  
7591
7592  /*
7593  * - LGPL
7594  *
7595  * Pagination item
7596  * 
7597  */
7598
7599
7600 /**
7601  * @class Roo.bootstrap.PaginationItem
7602  * @extends Roo.bootstrap.Component
7603  * Bootstrap PaginationItem class
7604  * @cfg {String} html text
7605  * @cfg {String} href the link
7606  * @cfg {Boolean} preventDefault (true | false) default true
7607  * @cfg {Boolean} active (true | false) default false
7608  * @cfg {Boolean} disabled default false
7609  * 
7610  * 
7611  * @constructor
7612  * Create a new PaginationItem
7613  * @param {Object} config The config object
7614  */
7615
7616
7617 Roo.bootstrap.PaginationItem = function(config){
7618     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7619     this.addEvents({
7620         // raw events
7621         /**
7622          * @event click
7623          * The raw click event for the entire grid.
7624          * @param {Roo.EventObject} e
7625          */
7626         "click" : true
7627     });
7628 };
7629
7630 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7631     
7632     href : false,
7633     html : false,
7634     preventDefault: true,
7635     active : false,
7636     cls : false,
7637     disabled: false,
7638     
7639     getAutoCreate : function(){
7640         var cfg= {
7641             tag: 'li',
7642             cn: [
7643                 {
7644                     tag : 'a',
7645                     href : this.href ? this.href : '#',
7646                     html : this.html ? this.html : ''
7647                 }
7648             ]
7649         };
7650         
7651         if(this.cls){
7652             cfg.cls = this.cls;
7653         }
7654         
7655         if(this.disabled){
7656             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7657         }
7658         
7659         if(this.active){
7660             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7661         }
7662         
7663         return cfg;
7664     },
7665     
7666     initEvents: function() {
7667         
7668         this.el.on('click', this.onClick, this);
7669         
7670     },
7671     onClick : function(e)
7672     {
7673         Roo.log('PaginationItem on click ');
7674         if(this.preventDefault){
7675             e.preventDefault();
7676         }
7677         
7678         if(this.disabled){
7679             return;
7680         }
7681         
7682         this.fireEvent('click', this, e);
7683     }
7684    
7685 });
7686
7687  
7688
7689  /*
7690  * - LGPL
7691  *
7692  * slider
7693  * 
7694  */
7695
7696
7697 /**
7698  * @class Roo.bootstrap.Slider
7699  * @extends Roo.bootstrap.Component
7700  * Bootstrap Slider class
7701  *    
7702  * @constructor
7703  * Create a new Slider
7704  * @param {Object} config The config object
7705  */
7706
7707 Roo.bootstrap.Slider = function(config){
7708     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7709 };
7710
7711 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7712     
7713     getAutoCreate : function(){
7714         
7715         var cfg = {
7716             tag: 'div',
7717             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7718             cn: [
7719                 {
7720                     tag: 'a',
7721                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7722                 }
7723             ]
7724         };
7725         
7726         return cfg;
7727     }
7728    
7729 });
7730
7731  /*
7732  * Based on:
7733  * Ext JS Library 1.1.1
7734  * Copyright(c) 2006-2007, Ext JS, LLC.
7735  *
7736  * Originally Released Under LGPL - original licence link has changed is not relivant.
7737  *
7738  * Fork - LGPL
7739  * <script type="text/javascript">
7740  */
7741  /**
7742  * @extends Roo.dd.DDProxy
7743  * @class Roo.grid.SplitDragZone
7744  * Support for Column Header resizing
7745  * @constructor
7746  * @param {Object} config
7747  */
7748 // private
7749 // This is a support class used internally by the Grid components
7750 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7751     this.grid = grid;
7752     this.view = grid.getView();
7753     this.proxy = this.view.resizeProxy;
7754     Roo.grid.SplitDragZone.superclass.constructor.call(
7755         this,
7756         hd, // ID
7757         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7758         {  // CONFIG
7759             dragElId : Roo.id(this.proxy.dom),
7760             resizeFrame:false
7761         }
7762     );
7763     
7764     this.setHandleElId(Roo.id(hd));
7765     if (hd2 !== false) {
7766         this.setOuterHandleElId(Roo.id(hd2));
7767     }
7768     
7769     this.scroll = false;
7770 };
7771 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7772     fly: Roo.Element.fly,
7773
7774     b4StartDrag : function(x, y){
7775         this.view.headersDisabled = true;
7776         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7777                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7778         );
7779         this.proxy.setHeight(h);
7780         
7781         // for old system colWidth really stored the actual width?
7782         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7783         // which in reality did not work.. - it worked only for fixed sizes
7784         // for resizable we need to use actual sizes.
7785         var w = this.cm.getColumnWidth(this.cellIndex);
7786         if (!this.view.mainWrap) {
7787             // bootstrap.
7788             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7789         }
7790         
7791         
7792         
7793         // this was w-this.grid.minColumnWidth;
7794         // doesnt really make sense? - w = thie curren width or the rendered one?
7795         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7796         this.resetConstraints();
7797         this.setXConstraint(minw, 1000);
7798         this.setYConstraint(0, 0);
7799         this.minX = x - minw;
7800         this.maxX = x + 1000;
7801         this.startPos = x;
7802         if (!this.view.mainWrap) { // this is Bootstrap code..
7803             this.getDragEl().style.display='block';
7804         }
7805         
7806         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7807     },
7808
7809
7810     handleMouseDown : function(e){
7811         ev = Roo.EventObject.setEvent(e);
7812         var t = this.fly(ev.getTarget());
7813         if(t.hasClass("x-grid-split")){
7814             this.cellIndex = this.view.getCellIndex(t.dom);
7815             this.split = t.dom;
7816             this.cm = this.grid.colModel;
7817             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7818                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7819             }
7820         }
7821     },
7822
7823     endDrag : function(e){
7824         this.view.headersDisabled = false;
7825         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7826         var diff = endX - this.startPos;
7827         // 
7828         var w = this.cm.getColumnWidth(this.cellIndex);
7829         if (!this.view.mainWrap) {
7830             w = 0;
7831         }
7832         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7833     },
7834
7835     autoOffset : function(){
7836         this.setDelta(0,0);
7837     }
7838 });/*
7839  * Based on:
7840  * Ext JS Library 1.1.1
7841  * Copyright(c) 2006-2007, Ext JS, LLC.
7842  *
7843  * Originally Released Under LGPL - original licence link has changed is not relivant.
7844  *
7845  * Fork - LGPL
7846  * <script type="text/javascript">
7847  */
7848
7849 /**
7850  * @class Roo.grid.AbstractSelectionModel
7851  * @extends Roo.util.Observable
7852  * @abstract
7853  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7854  * implemented by descendant classes.  This class should not be directly instantiated.
7855  * @constructor
7856  */
7857 Roo.grid.AbstractSelectionModel = function(){
7858     this.locked = false;
7859     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7860 };
7861
7862 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7863     /** @ignore Called by the grid automatically. Do not call directly. */
7864     init : function(grid){
7865         this.grid = grid;
7866         this.initEvents();
7867     },
7868
7869     /**
7870      * Locks the selections.
7871      */
7872     lock : function(){
7873         this.locked = true;
7874     },
7875
7876     /**
7877      * Unlocks the selections.
7878      */
7879     unlock : function(){
7880         this.locked = false;
7881     },
7882
7883     /**
7884      * Returns true if the selections are locked.
7885      * @return {Boolean}
7886      */
7887     isLocked : function(){
7888         return this.locked;
7889     }
7890 });/*
7891  * Based on:
7892  * Ext JS Library 1.1.1
7893  * Copyright(c) 2006-2007, Ext JS, LLC.
7894  *
7895  * Originally Released Under LGPL - original licence link has changed is not relivant.
7896  *
7897  * Fork - LGPL
7898  * <script type="text/javascript">
7899  */
7900 /**
7901  * @extends Roo.grid.AbstractSelectionModel
7902  * @class Roo.grid.RowSelectionModel
7903  * The default SelectionModel used by {@link Roo.grid.Grid}.
7904  * It supports multiple selections and keyboard selection/navigation. 
7905  * @constructor
7906  * @param {Object} config
7907  */
7908 Roo.grid.RowSelectionModel = function(config){
7909     Roo.apply(this, config);
7910     this.selections = new Roo.util.MixedCollection(false, function(o){
7911         return o.id;
7912     });
7913
7914     this.last = false;
7915     this.lastActive = false;
7916
7917     this.addEvents({
7918         /**
7919         * @event selectionchange
7920         * Fires when the selection changes
7921         * @param {SelectionModel} this
7922         */
7923        "selectionchange" : true,
7924        /**
7925         * @event afterselectionchange
7926         * Fires after the selection changes (eg. by key press or clicking)
7927         * @param {SelectionModel} this
7928         */
7929        "afterselectionchange" : true,
7930        /**
7931         * @event beforerowselect
7932         * Fires when a row is selected being selected, return false to cancel.
7933         * @param {SelectionModel} this
7934         * @param {Number} rowIndex The selected index
7935         * @param {Boolean} keepExisting False if other selections will be cleared
7936         */
7937        "beforerowselect" : true,
7938        /**
7939         * @event rowselect
7940         * Fires when a row is selected.
7941         * @param {SelectionModel} this
7942         * @param {Number} rowIndex The selected index
7943         * @param {Roo.data.Record} r The record
7944         */
7945        "rowselect" : true,
7946        /**
7947         * @event rowdeselect
7948         * Fires when a row is deselected.
7949         * @param {SelectionModel} this
7950         * @param {Number} rowIndex The selected index
7951         */
7952         "rowdeselect" : true
7953     });
7954     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7955     this.locked = false;
7956 };
7957
7958 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7959     /**
7960      * @cfg {Boolean} singleSelect
7961      * True to allow selection of only one row at a time (defaults to false)
7962      */
7963     singleSelect : false,
7964
7965     // private
7966     initEvents : function(){
7967
7968         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7969             this.grid.on("mousedown", this.handleMouseDown, this);
7970         }else{ // allow click to work like normal
7971             this.grid.on("rowclick", this.handleDragableRowClick, this);
7972         }
7973         // bootstrap does not have a view..
7974         var view = this.grid.view ? this.grid.view : this.grid;
7975         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7976             "up" : function(e){
7977                 if(!e.shiftKey){
7978                     this.selectPrevious(e.shiftKey);
7979                 }else if(this.last !== false && this.lastActive !== false){
7980                     var last = this.last;
7981                     this.selectRange(this.last,  this.lastActive-1);
7982                     view.focusRow(this.lastActive);
7983                     if(last !== false){
7984                         this.last = last;
7985                     }
7986                 }else{
7987                     this.selectFirstRow();
7988                 }
7989                 this.fireEvent("afterselectionchange", this);
7990             },
7991             "down" : function(e){
7992                 if(!e.shiftKey){
7993                     this.selectNext(e.shiftKey);
7994                 }else if(this.last !== false && this.lastActive !== false){
7995                     var last = this.last;
7996                     this.selectRange(this.last,  this.lastActive+1);
7997                     view.focusRow(this.lastActive);
7998                     if(last !== false){
7999                         this.last = last;
8000                     }
8001                 }else{
8002                     this.selectFirstRow();
8003                 }
8004                 this.fireEvent("afterselectionchange", this);
8005             },
8006             scope: this
8007         });
8008
8009          
8010         view.on("refresh", this.onRefresh, this);
8011         view.on("rowupdated", this.onRowUpdated, this);
8012         view.on("rowremoved", this.onRemove, this);
8013     },
8014
8015     // private
8016     onRefresh : function(){
8017         var ds = this.grid.ds, i, v = this.grid.view;
8018         var s = this.selections;
8019         s.each(function(r){
8020             if((i = ds.indexOfId(r.id)) != -1){
8021                 v.onRowSelect(i);
8022                 s.add(ds.getAt(i)); // updating the selection relate data
8023             }else{
8024                 s.remove(r);
8025             }
8026         });
8027     },
8028
8029     // private
8030     onRemove : function(v, index, r){
8031         this.selections.remove(r);
8032     },
8033
8034     // private
8035     onRowUpdated : function(v, index, r){
8036         if(this.isSelected(r)){
8037             v.onRowSelect(index);
8038         }
8039     },
8040
8041     /**
8042      * Select records.
8043      * @param {Array} records The records to select
8044      * @param {Boolean} keepExisting (optional) True to keep existing selections
8045      */
8046     selectRecords : function(records, keepExisting){
8047         if(!keepExisting){
8048             this.clearSelections();
8049         }
8050         var ds = this.grid.ds;
8051         for(var i = 0, len = records.length; i < len; i++){
8052             this.selectRow(ds.indexOf(records[i]), true);
8053         }
8054     },
8055
8056     /**
8057      * Gets the number of selected rows.
8058      * @return {Number}
8059      */
8060     getCount : function(){
8061         return this.selections.length;
8062     },
8063
8064     /**
8065      * Selects the first row in the grid.
8066      */
8067     selectFirstRow : function(){
8068         this.selectRow(0);
8069     },
8070
8071     /**
8072      * Select the last row.
8073      * @param {Boolean} keepExisting (optional) True to keep existing selections
8074      */
8075     selectLastRow : function(keepExisting){
8076         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8077     },
8078
8079     /**
8080      * Selects the row immediately following the last selected row.
8081      * @param {Boolean} keepExisting (optional) True to keep existing selections
8082      */
8083     selectNext : function(keepExisting){
8084         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8085             this.selectRow(this.last+1, keepExisting);
8086             var view = this.grid.view ? this.grid.view : this.grid;
8087             view.focusRow(this.last);
8088         }
8089     },
8090
8091     /**
8092      * Selects the row that precedes the last selected row.
8093      * @param {Boolean} keepExisting (optional) True to keep existing selections
8094      */
8095     selectPrevious : function(keepExisting){
8096         if(this.last){
8097             this.selectRow(this.last-1, keepExisting);
8098             var view = this.grid.view ? this.grid.view : this.grid;
8099             view.focusRow(this.last);
8100         }
8101     },
8102
8103     /**
8104      * Returns the selected records
8105      * @return {Array} Array of selected records
8106      */
8107     getSelections : function(){
8108         return [].concat(this.selections.items);
8109     },
8110
8111     /**
8112      * Returns the first selected record.
8113      * @return {Record}
8114      */
8115     getSelected : function(){
8116         return this.selections.itemAt(0);
8117     },
8118
8119
8120     /**
8121      * Clears all selections.
8122      */
8123     clearSelections : function(fast){
8124         if(this.locked) {
8125             return;
8126         }
8127         if(fast !== true){
8128             var ds = this.grid.ds;
8129             var s = this.selections;
8130             s.each(function(r){
8131                 this.deselectRow(ds.indexOfId(r.id));
8132             }, this);
8133             s.clear();
8134         }else{
8135             this.selections.clear();
8136         }
8137         this.last = false;
8138     },
8139
8140
8141     /**
8142      * Selects all rows.
8143      */
8144     selectAll : function(){
8145         if(this.locked) {
8146             return;
8147         }
8148         this.selections.clear();
8149         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8150             this.selectRow(i, true);
8151         }
8152     },
8153
8154     /**
8155      * Returns True if there is a selection.
8156      * @return {Boolean}
8157      */
8158     hasSelection : function(){
8159         return this.selections.length > 0;
8160     },
8161
8162     /**
8163      * Returns True if the specified row is selected.
8164      * @param {Number/Record} record The record or index of the record to check
8165      * @return {Boolean}
8166      */
8167     isSelected : function(index){
8168         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8169         return (r && this.selections.key(r.id) ? true : false);
8170     },
8171
8172     /**
8173      * Returns True if the specified record id is selected.
8174      * @param {String} id The id of record to check
8175      * @return {Boolean}
8176      */
8177     isIdSelected : function(id){
8178         return (this.selections.key(id) ? true : false);
8179     },
8180
8181     // private
8182     handleMouseDown : function(e, t)
8183     {
8184         var view = this.grid.view ? this.grid.view : this.grid;
8185         var rowIndex;
8186         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8187             return;
8188         };
8189         if(e.shiftKey && this.last !== false){
8190             var last = this.last;
8191             this.selectRange(last, rowIndex, e.ctrlKey);
8192             this.last = last; // reset the last
8193             view.focusRow(rowIndex);
8194         }else{
8195             var isSelected = this.isSelected(rowIndex);
8196             if(e.button !== 0 && isSelected){
8197                 view.focusRow(rowIndex);
8198             }else if(e.ctrlKey && isSelected){
8199                 this.deselectRow(rowIndex);
8200             }else if(!isSelected){
8201                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8202                 view.focusRow(rowIndex);
8203             }
8204         }
8205         this.fireEvent("afterselectionchange", this);
8206     },
8207     // private
8208     handleDragableRowClick :  function(grid, rowIndex, e) 
8209     {
8210         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8211             this.selectRow(rowIndex, false);
8212             var view = this.grid.view ? this.grid.view : this.grid;
8213             view.focusRow(rowIndex);
8214              this.fireEvent("afterselectionchange", this);
8215         }
8216     },
8217     
8218     /**
8219      * Selects multiple rows.
8220      * @param {Array} rows Array of the indexes of the row to select
8221      * @param {Boolean} keepExisting (optional) True to keep existing selections
8222      */
8223     selectRows : function(rows, keepExisting){
8224         if(!keepExisting){
8225             this.clearSelections();
8226         }
8227         for(var i = 0, len = rows.length; i < len; i++){
8228             this.selectRow(rows[i], true);
8229         }
8230     },
8231
8232     /**
8233      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8234      * @param {Number} startRow The index of the first row in the range
8235      * @param {Number} endRow The index of the last row in the range
8236      * @param {Boolean} keepExisting (optional) True to retain existing selections
8237      */
8238     selectRange : function(startRow, endRow, keepExisting){
8239         if(this.locked) {
8240             return;
8241         }
8242         if(!keepExisting){
8243             this.clearSelections();
8244         }
8245         if(startRow <= endRow){
8246             for(var i = startRow; i <= endRow; i++){
8247                 this.selectRow(i, true);
8248             }
8249         }else{
8250             for(var i = startRow; i >= endRow; i--){
8251                 this.selectRow(i, true);
8252             }
8253         }
8254     },
8255
8256     /**
8257      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8258      * @param {Number} startRow The index of the first row in the range
8259      * @param {Number} endRow The index of the last row in the range
8260      */
8261     deselectRange : function(startRow, endRow, preventViewNotify){
8262         if(this.locked) {
8263             return;
8264         }
8265         for(var i = startRow; i <= endRow; i++){
8266             this.deselectRow(i, preventViewNotify);
8267         }
8268     },
8269
8270     /**
8271      * Selects a row.
8272      * @param {Number} row The index of the row to select
8273      * @param {Boolean} keepExisting (optional) True to keep existing selections
8274      */
8275     selectRow : function(index, keepExisting, preventViewNotify){
8276         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8277             return;
8278         }
8279         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8280             if(!keepExisting || this.singleSelect){
8281                 this.clearSelections();
8282             }
8283             var r = this.grid.ds.getAt(index);
8284             this.selections.add(r);
8285             this.last = this.lastActive = index;
8286             if(!preventViewNotify){
8287                 var view = this.grid.view ? this.grid.view : this.grid;
8288                 view.onRowSelect(index);
8289             }
8290             this.fireEvent("rowselect", this, index, r);
8291             this.fireEvent("selectionchange", this);
8292         }
8293     },
8294
8295     /**
8296      * Deselects a row.
8297      * @param {Number} row The index of the row to deselect
8298      */
8299     deselectRow : function(index, preventViewNotify){
8300         if(this.locked) {
8301             return;
8302         }
8303         if(this.last == index){
8304             this.last = false;
8305         }
8306         if(this.lastActive == index){
8307             this.lastActive = false;
8308         }
8309         var r = this.grid.ds.getAt(index);
8310         this.selections.remove(r);
8311         if(!preventViewNotify){
8312             var view = this.grid.view ? this.grid.view : this.grid;
8313             view.onRowDeselect(index);
8314         }
8315         this.fireEvent("rowdeselect", this, index);
8316         this.fireEvent("selectionchange", this);
8317     },
8318
8319     // private
8320     restoreLast : function(){
8321         if(this._last){
8322             this.last = this._last;
8323         }
8324     },
8325
8326     // private
8327     acceptsNav : function(row, col, cm){
8328         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8329     },
8330
8331     // private
8332     onEditorKey : function(field, e){
8333         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8334         if(k == e.TAB){
8335             e.stopEvent();
8336             ed.completeEdit();
8337             if(e.shiftKey){
8338                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8339             }else{
8340                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8341             }
8342         }else if(k == e.ENTER && !e.ctrlKey){
8343             e.stopEvent();
8344             ed.completeEdit();
8345             if(e.shiftKey){
8346                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8347             }else{
8348                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8349             }
8350         }else if(k == e.ESC){
8351             ed.cancelEdit();
8352         }
8353         if(newCell){
8354             g.startEditing(newCell[0], newCell[1]);
8355         }
8356     }
8357 });/*
8358  * Based on:
8359  * Ext JS Library 1.1.1
8360  * Copyright(c) 2006-2007, Ext JS, LLC.
8361  *
8362  * Originally Released Under LGPL - original licence link has changed is not relivant.
8363  *
8364  * Fork - LGPL
8365  * <script type="text/javascript">
8366  */
8367  
8368
8369 /**
8370  * @class Roo.grid.ColumnModel
8371  * @extends Roo.util.Observable
8372  * This is the default implementation of a ColumnModel used by the Grid. It defines
8373  * the columns in the grid.
8374  * <br>Usage:<br>
8375  <pre><code>
8376  var colModel = new Roo.grid.ColumnModel([
8377         {header: "Ticker", width: 60, sortable: true, locked: true},
8378         {header: "Company Name", width: 150, sortable: true},
8379         {header: "Market Cap.", width: 100, sortable: true},
8380         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8381         {header: "Employees", width: 100, sortable: true, resizable: false}
8382  ]);
8383  </code></pre>
8384  * <p>
8385  
8386  * The config options listed for this class are options which may appear in each
8387  * individual column definition.
8388  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8389  * @constructor
8390  * @param {Object} config An Array of column config objects. See this class's
8391  * config objects for details.
8392 */
8393 Roo.grid.ColumnModel = function(config){
8394         /**
8395      * The config passed into the constructor
8396      */
8397     this.config = []; //config;
8398     this.lookup = {};
8399
8400     // if no id, create one
8401     // if the column does not have a dataIndex mapping,
8402     // map it to the order it is in the config
8403     for(var i = 0, len = config.length; i < len; i++){
8404         this.addColumn(config[i]);
8405         
8406     }
8407
8408     /**
8409      * The width of columns which have no width specified (defaults to 100)
8410      * @type Number
8411      */
8412     this.defaultWidth = 100;
8413
8414     /**
8415      * Default sortable of columns which have no sortable specified (defaults to false)
8416      * @type Boolean
8417      */
8418     this.defaultSortable = false;
8419
8420     this.addEvents({
8421         /**
8422              * @event widthchange
8423              * Fires when the width of a column changes.
8424              * @param {ColumnModel} this
8425              * @param {Number} columnIndex The column index
8426              * @param {Number} newWidth The new width
8427              */
8428             "widthchange": true,
8429         /**
8430              * @event headerchange
8431              * Fires when the text of a header changes.
8432              * @param {ColumnModel} this
8433              * @param {Number} columnIndex The column index
8434              * @param {Number} newText The new header text
8435              */
8436             "headerchange": true,
8437         /**
8438              * @event hiddenchange
8439              * Fires when a column is hidden or "unhidden".
8440              * @param {ColumnModel} this
8441              * @param {Number} columnIndex The column index
8442              * @param {Boolean} hidden true if hidden, false otherwise
8443              */
8444             "hiddenchange": true,
8445             /**
8446          * @event columnmoved
8447          * Fires when a column is moved.
8448          * @param {ColumnModel} this
8449          * @param {Number} oldIndex
8450          * @param {Number} newIndex
8451          */
8452         "columnmoved" : true,
8453         /**
8454          * @event columlockchange
8455          * Fires when a column's locked state is changed
8456          * @param {ColumnModel} this
8457          * @param {Number} colIndex
8458          * @param {Boolean} locked true if locked
8459          */
8460         "columnlockchange" : true
8461     });
8462     Roo.grid.ColumnModel.superclass.constructor.call(this);
8463 };
8464 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8465     /**
8466      * @cfg {String} header [required] The header text to display in the Grid view.
8467      */
8468         /**
8469      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8470      */
8471         /**
8472      * @cfg {String} smHeader Header at Bootsrap Small width
8473      */
8474         /**
8475      * @cfg {String} mdHeader Header at Bootsrap Medium width
8476      */
8477         /**
8478      * @cfg {String} lgHeader Header at Bootsrap Large width
8479      */
8480         /**
8481      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8482      */
8483     /**
8484      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
8485      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8486      * specified, the column's index is used as an index into the Record's data Array.
8487      */
8488     /**
8489      * @cfg {Number} width  The initial width in pixels of the column. Using this
8490      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8491      */
8492     /**
8493      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8494      * Defaults to the value of the {@link #defaultSortable} property.
8495      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8496      */
8497     /**
8498      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
8499      */
8500     /**
8501      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
8502      */
8503     /**
8504      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
8505      */
8506     /**
8507      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
8508      */
8509     /**
8510      * @cfg {Function} renderer A function used to generate HTML markup for a cell
8511      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8512      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8513      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8514      */
8515        /**
8516      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
8517      */
8518     /**
8519      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
8520      */
8521     /**
8522      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
8523      */
8524     /**
8525      * @cfg {String} cursor ( auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|e-resize|n-resize|ne-resize|nw-resize|s-resize|se-resize|sw-resize|w-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing)
8526      */
8527     /**
8528      * @cfg {String} tooltip mouse over tooltip text
8529      */
8530     /**
8531      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
8532      */
8533     /**
8534      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8535      */
8536     /**
8537      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8538      */
8539     /**
8540      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
8541      */
8542         /**
8543      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
8544      */
8545     /**
8546      * Returns the id of the column at the specified index.
8547      * @param {Number} index The column index
8548      * @return {String} the id
8549      */
8550     getColumnId : function(index){
8551         return this.config[index].id;
8552     },
8553
8554     /**
8555      * Returns the column for a specified id.
8556      * @param {String} id The column id
8557      * @return {Object} the column
8558      */
8559     getColumnById : function(id){
8560         return this.lookup[id];
8561     },
8562
8563     
8564     /**
8565      * Returns the column Object for a specified dataIndex.
8566      * @param {String} dataIndex The column dataIndex
8567      * @return {Object|Boolean} the column or false if not found
8568      */
8569     getColumnByDataIndex: function(dataIndex){
8570         var index = this.findColumnIndex(dataIndex);
8571         return index > -1 ? this.config[index] : false;
8572     },
8573     
8574     /**
8575      * Returns the index for a specified column id.
8576      * @param {String} id The column id
8577      * @return {Number} the index, or -1 if not found
8578      */
8579     getIndexById : function(id){
8580         for(var i = 0, len = this.config.length; i < len; i++){
8581             if(this.config[i].id == id){
8582                 return i;
8583             }
8584         }
8585         return -1;
8586     },
8587     
8588     /**
8589      * Returns the index for a specified column dataIndex.
8590      * @param {String} dataIndex The column dataIndex
8591      * @return {Number} the index, or -1 if not found
8592      */
8593     
8594     findColumnIndex : function(dataIndex){
8595         for(var i = 0, len = this.config.length; i < len; i++){
8596             if(this.config[i].dataIndex == dataIndex){
8597                 return i;
8598             }
8599         }
8600         return -1;
8601     },
8602     
8603     
8604     moveColumn : function(oldIndex, newIndex){
8605         var c = this.config[oldIndex];
8606         this.config.splice(oldIndex, 1);
8607         this.config.splice(newIndex, 0, c);
8608         this.dataMap = null;
8609         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8610     },
8611
8612     isLocked : function(colIndex){
8613         return this.config[colIndex].locked === true;
8614     },
8615
8616     setLocked : function(colIndex, value, suppressEvent){
8617         if(this.isLocked(colIndex) == value){
8618             return;
8619         }
8620         this.config[colIndex].locked = value;
8621         if(!suppressEvent){
8622             this.fireEvent("columnlockchange", this, colIndex, value);
8623         }
8624     },
8625
8626     getTotalLockedWidth : function(){
8627         var totalWidth = 0;
8628         for(var i = 0; i < this.config.length; i++){
8629             if(this.isLocked(i) && !this.isHidden(i)){
8630                 this.totalWidth += this.getColumnWidth(i);
8631             }
8632         }
8633         return totalWidth;
8634     },
8635
8636     getLockedCount : function(){
8637         for(var i = 0, len = this.config.length; i < len; i++){
8638             if(!this.isLocked(i)){
8639                 return i;
8640             }
8641         }
8642         
8643         return this.config.length;
8644     },
8645
8646     /**
8647      * Returns the number of columns.
8648      * @return {Number}
8649      */
8650     getColumnCount : function(visibleOnly){
8651         if(visibleOnly === true){
8652             var c = 0;
8653             for(var i = 0, len = this.config.length; i < len; i++){
8654                 if(!this.isHidden(i)){
8655                     c++;
8656                 }
8657             }
8658             return c;
8659         }
8660         return this.config.length;
8661     },
8662
8663     /**
8664      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8665      * @param {Function} fn
8666      * @param {Object} scope (optional)
8667      * @return {Array} result
8668      */
8669     getColumnsBy : function(fn, scope){
8670         var r = [];
8671         for(var i = 0, len = this.config.length; i < len; i++){
8672             var c = this.config[i];
8673             if(fn.call(scope||this, c, i) === true){
8674                 r[r.length] = c;
8675             }
8676         }
8677         return r;
8678     },
8679
8680     /**
8681      * Returns true if the specified column is sortable.
8682      * @param {Number} col The column index
8683      * @return {Boolean}
8684      */
8685     isSortable : function(col){
8686         if(typeof this.config[col].sortable == "undefined"){
8687             return this.defaultSortable;
8688         }
8689         return this.config[col].sortable;
8690     },
8691
8692     /**
8693      * Returns the rendering (formatting) function defined for the column.
8694      * @param {Number} col The column index.
8695      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8696      */
8697     getRenderer : function(col){
8698         if(!this.config[col].renderer){
8699             return Roo.grid.ColumnModel.defaultRenderer;
8700         }
8701         return this.config[col].renderer;
8702     },
8703
8704     /**
8705      * Sets the rendering (formatting) function for a column.
8706      * @param {Number} col The column index
8707      * @param {Function} fn The function to use to process the cell's raw data
8708      * to return HTML markup for the grid view. The render function is called with
8709      * the following parameters:<ul>
8710      * <li>Data value.</li>
8711      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8712      * <li>css A CSS style string to apply to the table cell.</li>
8713      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8714      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8715      * <li>Row index</li>
8716      * <li>Column index</li>
8717      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8718      */
8719     setRenderer : function(col, fn){
8720         this.config[col].renderer = fn;
8721     },
8722
8723     /**
8724      * Returns the width for the specified column.
8725      * @param {Number} col The column index
8726      * @param (optional) {String} gridSize bootstrap width size.
8727      * @return {Number}
8728      */
8729     getColumnWidth : function(col, gridSize)
8730         {
8731                 var cfg = this.config[col];
8732                 
8733                 if (typeof(gridSize) == 'undefined') {
8734                         return cfg.width * 1 || this.defaultWidth;
8735                 }
8736                 if (gridSize === false) { // if we set it..
8737                         return cfg.width || false;
8738                 }
8739                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8740                 
8741                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8742                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8743                                 continue;
8744                         }
8745                         return cfg[ sizes[i] ];
8746                 }
8747                 return 1;
8748                 
8749     },
8750
8751     /**
8752      * Sets the width for a column.
8753      * @param {Number} col The column index
8754      * @param {Number} width The new width
8755      */
8756     setColumnWidth : function(col, width, suppressEvent){
8757         this.config[col].width = width;
8758         this.totalWidth = null;
8759         if(!suppressEvent){
8760              this.fireEvent("widthchange", this, col, width);
8761         }
8762     },
8763
8764     /**
8765      * Returns the total width of all columns.
8766      * @param {Boolean} includeHidden True to include hidden column widths
8767      * @return {Number}
8768      */
8769     getTotalWidth : function(includeHidden){
8770         if(!this.totalWidth){
8771             this.totalWidth = 0;
8772             for(var i = 0, len = this.config.length; i < len; i++){
8773                 if(includeHidden || !this.isHidden(i)){
8774                     this.totalWidth += this.getColumnWidth(i);
8775                 }
8776             }
8777         }
8778         return this.totalWidth;
8779     },
8780
8781     /**
8782      * Returns the header for the specified column.
8783      * @param {Number} col The column index
8784      * @return {String}
8785      */
8786     getColumnHeader : function(col){
8787         return this.config[col].header;
8788     },
8789
8790     /**
8791      * Sets the header for a column.
8792      * @param {Number} col The column index
8793      * @param {String} header The new header
8794      */
8795     setColumnHeader : function(col, header){
8796         this.config[col].header = header;
8797         this.fireEvent("headerchange", this, col, header);
8798     },
8799
8800     /**
8801      * Returns the tooltip for the specified column.
8802      * @param {Number} col The column index
8803      * @return {String}
8804      */
8805     getColumnTooltip : function(col){
8806             return this.config[col].tooltip;
8807     },
8808     /**
8809      * Sets the tooltip for a column.
8810      * @param {Number} col The column index
8811      * @param {String} tooltip The new tooltip
8812      */
8813     setColumnTooltip : function(col, tooltip){
8814             this.config[col].tooltip = tooltip;
8815     },
8816
8817     /**
8818      * Returns the dataIndex for the specified column.
8819      * @param {Number} col The column index
8820      * @return {Number}
8821      */
8822     getDataIndex : function(col){
8823         return this.config[col].dataIndex;
8824     },
8825
8826     /**
8827      * Sets the dataIndex for a column.
8828      * @param {Number} col The column index
8829      * @param {Number} dataIndex The new dataIndex
8830      */
8831     setDataIndex : function(col, dataIndex){
8832         this.config[col].dataIndex = dataIndex;
8833     },
8834
8835     
8836     
8837     /**
8838      * Returns true if the cell is editable.
8839      * @param {Number} colIndex The column index
8840      * @param {Number} rowIndex The row index - this is nto actually used..?
8841      * @return {Boolean}
8842      */
8843     isCellEditable : function(colIndex, rowIndex){
8844         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8845     },
8846
8847     /**
8848      * Returns the editor defined for the cell/column.
8849      * return false or null to disable editing.
8850      * @param {Number} colIndex The column index
8851      * @param {Number} rowIndex The row index
8852      * @return {Object}
8853      */
8854     getCellEditor : function(colIndex, rowIndex){
8855         return this.config[colIndex].editor;
8856     },
8857
8858     /**
8859      * Sets if a column is editable.
8860      * @param {Number} col The column index
8861      * @param {Boolean} editable True if the column is editable
8862      */
8863     setEditable : function(col, editable){
8864         this.config[col].editable = editable;
8865     },
8866
8867
8868     /**
8869      * Returns true if the column is hidden.
8870      * @param {Number} colIndex The column index
8871      * @return {Boolean}
8872      */
8873     isHidden : function(colIndex){
8874         return this.config[colIndex].hidden;
8875     },
8876
8877
8878     /**
8879      * Returns true if the column width cannot be changed
8880      */
8881     isFixed : function(colIndex){
8882         return this.config[colIndex].fixed;
8883     },
8884
8885     /**
8886      * Returns true if the column can be resized
8887      * @return {Boolean}
8888      */
8889     isResizable : function(colIndex){
8890         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8891     },
8892     /**
8893      * Sets if a column is hidden.
8894      * @param {Number} colIndex The column index
8895      * @param {Boolean} hidden True if the column is hidden
8896      */
8897     setHidden : function(colIndex, hidden){
8898         this.config[colIndex].hidden = hidden;
8899         this.totalWidth = null;
8900         this.fireEvent("hiddenchange", this, colIndex, hidden);
8901     },
8902
8903     /**
8904      * Sets the editor for a column.
8905      * @param {Number} col The column index
8906      * @param {Object} editor The editor object
8907      */
8908     setEditor : function(col, editor){
8909         this.config[col].editor = editor;
8910     },
8911     /**
8912      * Add a column (experimental...) - defaults to adding to the end..
8913      * @param {Object} config 
8914     */
8915     addColumn : function(c)
8916     {
8917     
8918         var i = this.config.length;
8919         this.config[i] = c;
8920         
8921         if(typeof c.dataIndex == "undefined"){
8922             c.dataIndex = i;
8923         }
8924         if(typeof c.renderer == "string"){
8925             c.renderer = Roo.util.Format[c.renderer];
8926         }
8927         if(typeof c.id == "undefined"){
8928             c.id = Roo.id();
8929         }
8930         if(c.editor && c.editor.xtype){
8931             c.editor  = Roo.factory(c.editor, Roo.grid);
8932         }
8933         if(c.editor && c.editor.isFormField){
8934             c.editor = new Roo.grid.GridEditor(c.editor);
8935         }
8936         this.lookup[c.id] = c;
8937     }
8938     
8939 });
8940
8941 Roo.grid.ColumnModel.defaultRenderer = function(value)
8942 {
8943     if(typeof value == "object") {
8944         return value;
8945     }
8946         if(typeof value == "string" && value.length < 1){
8947             return "&#160;";
8948         }
8949     
8950         return String.format("{0}", value);
8951 };
8952
8953 // Alias for backwards compatibility
8954 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8955 /*
8956  * Based on:
8957  * Ext JS Library 1.1.1
8958  * Copyright(c) 2006-2007, Ext JS, LLC.
8959  *
8960  * Originally Released Under LGPL - original licence link has changed is not relivant.
8961  *
8962  * Fork - LGPL
8963  * <script type="text/javascript">
8964  */
8965  
8966 /**
8967  * @class Roo.LoadMask
8968  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8969  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8970  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8971  * element's UpdateManager load indicator and will be destroyed after the initial load.
8972  * @constructor
8973  * Create a new LoadMask
8974  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8975  * @param {Object} config The config object
8976  */
8977 Roo.LoadMask = function(el, config){
8978     this.el = Roo.get(el);
8979     Roo.apply(this, config);
8980     if(this.store){
8981         this.store.on('beforeload', this.onBeforeLoad, this);
8982         this.store.on('load', this.onLoad, this);
8983         this.store.on('loadexception', this.onLoadException, this);
8984         this.removeMask = false;
8985     }else{
8986         var um = this.el.getUpdateManager();
8987         um.showLoadIndicator = false; // disable the default indicator
8988         um.on('beforeupdate', this.onBeforeLoad, this);
8989         um.on('update', this.onLoad, this);
8990         um.on('failure', this.onLoad, this);
8991         this.removeMask = true;
8992     }
8993 };
8994
8995 Roo.LoadMask.prototype = {
8996     /**
8997      * @cfg {Boolean} removeMask
8998      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8999      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
9000      */
9001     removeMask : false,
9002     /**
9003      * @cfg {String} msg
9004      * The text to display in a centered loading message box (defaults to 'Loading...')
9005      */
9006     msg : 'Loading...',
9007     /**
9008      * @cfg {String} msgCls
9009      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9010      */
9011     msgCls : 'x-mask-loading',
9012
9013     /**
9014      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9015      * @type Boolean
9016      */
9017     disabled: false,
9018
9019     /**
9020      * Disables the mask to prevent it from being displayed
9021      */
9022     disable : function(){
9023        this.disabled = true;
9024     },
9025
9026     /**
9027      * Enables the mask so that it can be displayed
9028      */
9029     enable : function(){
9030         this.disabled = false;
9031     },
9032     
9033     onLoadException : function()
9034     {
9035         Roo.log(arguments);
9036         
9037         if (typeof(arguments[3]) != 'undefined') {
9038             Roo.MessageBox.alert("Error loading",arguments[3]);
9039         } 
9040         /*
9041         try {
9042             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9043                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9044             }   
9045         } catch(e) {
9046             
9047         }
9048         */
9049     
9050         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9051     },
9052     // private
9053     onLoad : function()
9054     {
9055         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9056     },
9057
9058     // private
9059     onBeforeLoad : function(){
9060         if(!this.disabled){
9061             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9062         }
9063     },
9064
9065     // private
9066     destroy : function(){
9067         if(this.store){
9068             this.store.un('beforeload', this.onBeforeLoad, this);
9069             this.store.un('load', this.onLoad, this);
9070             this.store.un('loadexception', this.onLoadException, this);
9071         }else{
9072             var um = this.el.getUpdateManager();
9073             um.un('beforeupdate', this.onBeforeLoad, this);
9074             um.un('update', this.onLoad, this);
9075             um.un('failure', this.onLoad, this);
9076         }
9077     }
9078 };/**
9079  * @class Roo.bootstrap.Table
9080  * @licence LGBL
9081  * @extends Roo.bootstrap.Component
9082  * @children Roo.bootstrap.TableBody
9083  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9084  * Similar to Roo.grid.Grid
9085  * <pre><code>
9086  var table = Roo.factory({
9087     xtype : 'Table',
9088     xns : Roo.bootstrap,
9089     autoSizeColumns: true,
9090     
9091     
9092     store : {
9093         xtype : 'Store',
9094         xns : Roo.data,
9095         remoteSort : true,
9096         sortInfo : { direction : 'ASC', field: 'name' },
9097         proxy : {
9098            xtype : 'HttpProxy',
9099            xns : Roo.data,
9100            method : 'GET',
9101            url : 'https://example.com/some.data.url.json'
9102         },
9103         reader : {
9104            xtype : 'JsonReader',
9105            xns : Roo.data,
9106            fields : [ 'id', 'name', whatever' ],
9107            id : 'id',
9108            root : 'data'
9109         }
9110     },
9111     cm : [
9112         {
9113             xtype : 'ColumnModel',
9114             xns : Roo.grid,
9115             align : 'center',
9116             cursor : 'pointer',
9117             dataIndex : 'is_in_group',
9118             header : "Name",
9119             sortable : true,
9120             renderer : function(v, x , r) {  
9121             
9122                 return String.format("{0}", v)
9123             }
9124             width : 3
9125         } // more columns..
9126     ],
9127     selModel : {
9128         xtype : 'RowSelectionModel',
9129         xns : Roo.bootstrap.Table
9130         // you can add listeners to catch selection change here....
9131     }
9132      
9133
9134  });
9135  // set any options
9136  grid.render(Roo.get("some-div"));
9137 </code></pre>
9138
9139 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9140
9141
9142
9143  *
9144  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9145  * @cfg {Roo.data.Store} store The data store to use
9146  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9147  * 
9148  * @cfg {String} cls table class
9149  *
9150  *
9151  * @cfg {string} empty_results  Text to display for no results 
9152  * @cfg {boolean} striped Should the rows be alternative striped
9153  * @cfg {boolean} bordered Add borders to the table
9154  * @cfg {boolean} hover Add hover highlighting
9155  * @cfg {boolean} condensed Format condensed
9156  * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
9157  *                also adds table-responsive (see bootstrap docs for details)
9158  * @cfg {Boolean} loadMask (true|false) default false
9159  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9160  * @cfg {Boolean} summaryFooterShow (true|false) generate tfoot for summary, default false
9161  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9162  * @cfg {Boolean} rowSelection (true|false) default false
9163  * @cfg {Boolean} cellSelection (true|false) default false
9164  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9165  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9166  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9167  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9168  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9169  * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS()
9170  *
9171  * 
9172  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9173  * 
9174  * @constructor
9175  * Create a new Table
9176  * @param {Object} config The config object
9177  */
9178
9179 Roo.bootstrap.Table = function(config)
9180 {
9181     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9182      
9183     // BC...
9184     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9185     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9186     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9187     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9188     
9189     this.view = this; // compat with grid.
9190     
9191     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9192     if (this.sm) {
9193         this.sm.grid = this;
9194         this.selModel = Roo.factory(this.sm, Roo.grid);
9195         this.sm = this.selModel;
9196         this.sm.xmodule = this.xmodule || false;
9197     }
9198     
9199     if (this.cm && typeof(this.cm.config) == 'undefined') {
9200         this.colModel = new Roo.grid.ColumnModel(this.cm);
9201         this.cm = this.colModel;
9202         this.cm.xmodule = this.xmodule || false;
9203     }
9204     if (this.store) {
9205         this.store= Roo.factory(this.store, Roo.data);
9206         this.ds = this.store;
9207         this.ds.xmodule = this.xmodule || false;
9208          
9209     }
9210     if (this.footer && this.store) {
9211         this.footer.dataSource = this.ds;
9212         this.footer = Roo.factory(this.footer);
9213     }
9214     
9215     /** @private */
9216     this.addEvents({
9217         /**
9218          * @event cellclick
9219          * Fires when a cell is clicked
9220          * @param {Roo.bootstrap.Table} this
9221          * @param {Roo.Element} el
9222          * @param {Number} rowIndex
9223          * @param {Number} columnIndex
9224          * @param {Roo.EventObject} e
9225          */
9226         "cellclick" : true,
9227         /**
9228          * @event celldblclick
9229          * Fires when a cell is double clicked
9230          * @param {Roo.bootstrap.Table} this
9231          * @param {Roo.Element} el
9232          * @param {Number} rowIndex
9233          * @param {Number} columnIndex
9234          * @param {Roo.EventObject} e
9235          */
9236         "celldblclick" : true,
9237         /**
9238          * @event rowclick
9239          * Fires when a row is clicked
9240          * @param {Roo.bootstrap.Table} this
9241          * @param {Roo.Element} el
9242          * @param {Number} rowIndex
9243          * @param {Roo.EventObject} e
9244          */
9245         "rowclick" : true,
9246         /**
9247          * @event rowdblclick
9248          * Fires when a row is double clicked
9249          * @param {Roo.bootstrap.Table} this
9250          * @param {Roo.Element} el
9251          * @param {Number} rowIndex
9252          * @param {Roo.EventObject} e
9253          */
9254         "rowdblclick" : true,
9255         /**
9256          * @event mouseover
9257          * Fires when a mouseover occur
9258          * @param {Roo.bootstrap.Table} this
9259          * @param {Roo.Element} el
9260          * @param {Number} rowIndex
9261          * @param {Number} columnIndex
9262          * @param {Roo.EventObject} e
9263          */
9264         "mouseover" : true,
9265         /**
9266          * @event mouseout
9267          * Fires when a mouseout occur
9268          * @param {Roo.bootstrap.Table} this
9269          * @param {Roo.Element} el
9270          * @param {Number} rowIndex
9271          * @param {Number} columnIndex
9272          * @param {Roo.EventObject} e
9273          */
9274         "mouseout" : true,
9275         /**
9276          * @event rowclass
9277          * Fires when a row is rendered, so you can change add a style to it.
9278          * @param {Roo.bootstrap.Table} this
9279          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9280          */
9281         'rowclass' : true,
9282           /**
9283          * @event rowsrendered
9284          * Fires when all the  rows have been rendered
9285          * @param {Roo.bootstrap.Table} this
9286          */
9287         'rowsrendered' : true,
9288         /**
9289          * @event contextmenu
9290          * The raw contextmenu event for the entire grid.
9291          * @param {Roo.EventObject} e
9292          */
9293         "contextmenu" : true,
9294         /**
9295          * @event rowcontextmenu
9296          * Fires when a row is right clicked
9297          * @param {Roo.bootstrap.Table} this
9298          * @param {Number} rowIndex
9299          * @param {Roo.EventObject} e
9300          */
9301         "rowcontextmenu" : true,
9302         /**
9303          * @event cellcontextmenu
9304          * Fires when a cell is right clicked
9305          * @param {Roo.bootstrap.Table} this
9306          * @param {Number} rowIndex
9307          * @param {Number} cellIndex
9308          * @param {Roo.EventObject} e
9309          */
9310          "cellcontextmenu" : true,
9311          /**
9312          * @event headercontextmenu
9313          * Fires when a header is right clicked
9314          * @param {Roo.bootstrap.Table} this
9315          * @param {Number} columnIndex
9316          * @param {Roo.EventObject} e
9317          */
9318         "headercontextmenu" : true,
9319         /**
9320          * @event mousedown
9321          * The raw mousedown event for the entire grid.
9322          * @param {Roo.EventObject} e
9323          */
9324         "mousedown" : true
9325         
9326     });
9327 };
9328
9329 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9330     
9331     cls: false,
9332     
9333     empty_results : '',
9334     striped : false,
9335     scrollBody : false,
9336     bordered: false,
9337     hover:  false,
9338     condensed : false,
9339     responsive : false,
9340     sm : false,
9341     cm : false,
9342     store : false,
9343     loadMask : false,
9344     footerShow : true,
9345     summaryFooterShow : false,
9346     headerShow : true,
9347     enableColumnResize: true,
9348     disableAutoSize: false,
9349   
9350     rowSelection : false,
9351     cellSelection : false,
9352     layout : false,
9353
9354     minColumnWidth : 50,
9355     
9356     // Roo.Element - the tbody
9357     bodyEl: false,  // <tbody> Roo.Element - thead element    
9358     headEl: false,  // <thead> Roo.Element - thead element
9359     resizeProxy : false, // proxy element for dragging?
9360
9361
9362     
9363     container: false, // used by gridpanel...
9364     
9365     lazyLoad : false,
9366     
9367     CSS : Roo.util.CSS,
9368     
9369     auto_hide_footer : false,
9370     
9371     view: false, // actually points to this..
9372     
9373     getAutoCreate : function()
9374     {
9375         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9376         
9377         cfg = {
9378             tag: 'table',
9379             cls : 'table', 
9380             cn : []
9381         };
9382         // this get's auto added by panel.Grid
9383         if (this.scrollBody) {
9384             cfg.cls += ' table-body-fixed';
9385         }    
9386         if (this.striped) {
9387             cfg.cls += ' table-striped';
9388         }
9389         
9390         if (this.hover) {
9391             cfg.cls += ' table-hover';
9392         }
9393         if (this.bordered) {
9394             cfg.cls += ' table-bordered';
9395         }
9396         if (this.condensed) {
9397             cfg.cls += ' table-condensed';
9398         }
9399         
9400         if (this.responsive) {
9401             cfg.cls += ' table-responsive';
9402         }
9403         
9404         if (this.cls) {
9405             cfg.cls+=  ' ' +this.cls;
9406         }
9407         
9408         
9409         
9410         if (this.layout) {
9411             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9412         }
9413         
9414         if(this.store || this.cm){
9415             if(this.headerShow){
9416                 cfg.cn.push(this.renderHeader());
9417             }
9418             
9419             cfg.cn.push(this.renderBody());
9420             
9421             if(this.footerShow){
9422                 cfg.cn.push(this.renderFooter());
9423             }
9424
9425             if(!this.footerShow && this.summaryFooterShow) {
9426                 cfg.cn.push(this.renderSummaryFooter());
9427             }
9428
9429             // where does this come from?
9430             //cfg.cls+=  ' TableGrid';
9431         }
9432         
9433         return { cn : [ cfg ] };
9434     },
9435     
9436     initEvents : function()
9437     {   
9438         if(!this.store || !this.cm){
9439             return;
9440         }
9441         if (this.selModel) {
9442             this.selModel.initEvents();
9443         }
9444         
9445         
9446         //Roo.log('initEvents with ds!!!!');
9447         
9448         this.bodyEl = this.el.select('tbody', true).first();
9449         this.headEl = this.el.select('thead', true).first();
9450         this.mainFoot = this.el.select('tfoot', true).first();
9451         
9452         
9453         
9454         
9455         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9456             e.on('click', this.sort, this);
9457         }, this);
9458         
9459         
9460         // why is this done????? = it breaks dialogs??
9461         //this.parent().el.setStyle('position', 'relative');
9462         
9463         
9464         if (this.footer) {
9465             this.footer.parentId = this.id;
9466             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9467             
9468             if(this.lazyLoad){
9469                 this.el.select('tfoot tr td').first().addClass('hide');
9470             }
9471         } 
9472         
9473         if(this.loadMask) {
9474             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9475         }
9476         
9477         this.store.on('load', this.onLoad, this);
9478         this.store.on('beforeload', this.onBeforeLoad, this);
9479         this.store.on('update', this.onUpdate, this);
9480         this.store.on('add', this.onAdd, this);
9481         this.store.on("clear", this.clear, this);
9482         
9483         this.el.on("contextmenu", this.onContextMenu, this);
9484         
9485         
9486         this.cm.on("headerchange", this.onHeaderChange, this);
9487         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9488
9489  //?? does bodyEl get replaced on render?
9490         this.bodyEl.on("click", this.onClick, this);
9491         this.bodyEl.on("dblclick", this.onDblClick, this);        
9492         this.bodyEl.on('scroll', this.onBodyScroll, this);
9493
9494         // guessing mainbody will work - this relays usually caught by selmodel at present.
9495         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9496   
9497   
9498         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9499         
9500   
9501         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9502             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9503         }
9504         
9505         this.initCSS();
9506     },
9507     // Compatibility with grid - we implement all the view features at present.
9508     getView : function()
9509     {
9510         return this;
9511     },
9512     
9513     initCSS : function()
9514     {
9515         if(this.disableAutoSize) {
9516             return;
9517         }
9518         
9519         var cm = this.cm, styles = [];
9520         this.CSS.removeStyleSheet(this.id + '-cssrules');
9521         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9522         // we can honour xs/sm/md/xl  as widths...
9523         // we first have to decide what widht we are currently at...
9524         var sz = Roo.getGridSize();
9525         
9526         var total = 0;
9527         var last = -1;
9528         var cols = []; // visable cols.
9529         var total_abs = 0;
9530         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9531             var w = cm.getColumnWidth(i, false);
9532             if(cm.isHidden(i)){
9533                 cols.push( { rel : false, abs : 0 });
9534                 continue;
9535             }
9536             if (w !== false) {
9537                 cols.push( { rel : false, abs : w });
9538                 total_abs += w;
9539                 last = i; // not really..
9540                 continue;
9541             }
9542             var w = cm.getColumnWidth(i, sz);
9543             if (w > 0) {
9544                 last = i
9545             }
9546             total += w;
9547             cols.push( { rel : w, abs : false });
9548         }
9549         
9550         var avail = this.bodyEl.dom.clientWidth - total_abs;
9551         
9552         var unitWidth = Math.floor(avail / total);
9553         var rem = avail - (unitWidth * total);
9554         
9555         var hidden, width, pos = 0 , splithide , left;
9556         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9557             
9558             hidden = 'display:none;';
9559             left = '';
9560             width  = 'width:0px;';
9561             splithide = '';
9562             if(!cm.isHidden(i)){
9563                 hidden = '';
9564                 
9565                 
9566                 // we can honour xs/sm/md/xl ?
9567                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9568                 if (w===0) {
9569                     hidden = 'display:none;';
9570                 }
9571                 // width should return a small number...
9572                 if (i == last) {
9573                     w+=rem; // add the remaining with..
9574                 }
9575                 pos += w;
9576                 left = "left:" + (pos -4) + "px;";
9577                 width = "width:" + w+ "px;";
9578                 
9579             }
9580             if (this.responsive) {
9581                 width = '';
9582                 left = '';
9583                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9584                 splithide = 'display: none;';
9585             }
9586             
9587             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9588             if (this.headEl) {
9589                 if (i == last) {
9590                     splithide = 'display:none;';
9591                 }
9592                 
9593                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9594                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9595                             // this is the popover version..
9596                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9597                 );
9598             }
9599             
9600         }
9601         //Roo.log(styles.join(''));
9602         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9603         
9604     },
9605     
9606     
9607     
9608     onContextMenu : function(e, t)
9609     {
9610         this.processEvent("contextmenu", e);
9611     },
9612     
9613     processEvent : function(name, e)
9614     {
9615         if (name != 'touchstart' ) {
9616             this.fireEvent(name, e);    
9617         }
9618         
9619         var t = e.getTarget();
9620         
9621         var cell = Roo.get(t);
9622         
9623         if(!cell){
9624             return;
9625         }
9626         
9627         if(cell.findParent('tfoot', false, true)){
9628             return;
9629         }
9630         
9631         if(cell.findParent('thead', false, true)){
9632             
9633             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9634                 cell = Roo.get(t).findParent('th', false, true);
9635                 if (!cell) {
9636                     Roo.log("failed to find th in thead?");
9637                     Roo.log(e.getTarget());
9638                     return;
9639                 }
9640             }
9641             
9642             var cellIndex = cell.dom.cellIndex;
9643             
9644             var ename = name == 'touchstart' ? 'click' : name;
9645             this.fireEvent("header" + ename, this, cellIndex, e);
9646             
9647             return;
9648         }
9649         
9650         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9651             cell = Roo.get(t).findParent('td', false, true);
9652             if (!cell) {
9653                 Roo.log("failed to find th in tbody?");
9654                 Roo.log(e.getTarget());
9655                 return;
9656             }
9657         }
9658         
9659         var row = cell.findParent('tr', false, true);
9660         var cellIndex = cell.dom.cellIndex;
9661         var rowIndex = row.dom.rowIndex - 1;
9662         
9663         if(row !== false){
9664             
9665             this.fireEvent("row" + name, this, rowIndex, e);
9666             
9667             if(cell !== false){
9668             
9669                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9670             }
9671         }
9672         
9673     },
9674     
9675     onMouseover : function(e, el)
9676     {
9677         var cell = Roo.get(el);
9678         
9679         if(!cell){
9680             return;
9681         }
9682         
9683         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9684             cell = cell.findParent('td', false, true);
9685         }
9686         
9687         var row = cell.findParent('tr', false, true);
9688         var cellIndex = cell.dom.cellIndex;
9689         var rowIndex = row.dom.rowIndex - 1; // start from 0
9690         
9691         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9692         
9693     },
9694     
9695     onMouseout : function(e, el)
9696     {
9697         var cell = Roo.get(el);
9698         
9699         if(!cell){
9700             return;
9701         }
9702         
9703         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9704             cell = cell.findParent('td', false, true);
9705         }
9706         
9707         var row = cell.findParent('tr', false, true);
9708         var cellIndex = cell.dom.cellIndex;
9709         var rowIndex = row.dom.rowIndex - 1; // start from 0
9710         
9711         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9712         
9713     },
9714     
9715     onClick : function(e, el)
9716     {
9717         var cell = Roo.get(el);
9718         
9719         if(!cell || (!this.cellSelection && !this.rowSelection)){
9720             return;
9721         }
9722         
9723         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9724             cell = cell.findParent('td', false, true);
9725         }
9726         
9727         if(!cell || typeof(cell) == 'undefined'){
9728             return;
9729         }
9730         
9731         var row = cell.findParent('tr', false, true);
9732         
9733         if(!row || typeof(row) == 'undefined'){
9734             return;
9735         }
9736         
9737         var cellIndex = cell.dom.cellIndex;
9738         var rowIndex = this.getRowIndex(row);
9739         
9740         // why??? - should these not be based on SelectionModel?
9741         //if(this.cellSelection){
9742             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9743         //}
9744         
9745         //if(this.rowSelection){
9746             this.fireEvent('rowclick', this, row, rowIndex, e);
9747         //}
9748          
9749     },
9750         
9751     onDblClick : function(e,el)
9752     {
9753         var cell = Roo.get(el);
9754         
9755         if(!cell || (!this.cellSelection && !this.rowSelection)){
9756             return;
9757         }
9758         
9759         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9760             cell = cell.findParent('td', false, true);
9761         }
9762         
9763         if(!cell || typeof(cell) == 'undefined'){
9764             return;
9765         }
9766         
9767         var row = cell.findParent('tr', false, true);
9768         
9769         if(!row || typeof(row) == 'undefined'){
9770             return;
9771         }
9772         
9773         var cellIndex = cell.dom.cellIndex;
9774         var rowIndex = this.getRowIndex(row);
9775         
9776         if(this.cellSelection){
9777             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9778         }
9779         
9780         if(this.rowSelection){
9781             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9782         }
9783     },
9784     findRowIndex : function(el)
9785     {
9786         var cell = Roo.get(el);
9787         if(!cell) {
9788             return false;
9789         }
9790         var row = cell.findParent('tr', false, true);
9791         
9792         if(!row || typeof(row) == 'undefined'){
9793             return false;
9794         }
9795         return this.getRowIndex(row);
9796     },
9797     sort : function(e,el)
9798     {
9799         var col = Roo.get(el);
9800         
9801         if(!col.hasClass('sortable')){
9802             return;
9803         }
9804         
9805         var sort = col.attr('sort');
9806         var dir = 'ASC';
9807         
9808         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9809             dir = 'DESC';
9810         }
9811         
9812         this.store.sortInfo = {field : sort, direction : dir};
9813         
9814         if (this.footer) {
9815             Roo.log("calling footer first");
9816             this.footer.onClick('first');
9817         } else {
9818         
9819             this.store.load({ params : { start : 0 } });
9820         }
9821     },
9822     
9823     renderHeader : function()
9824     {
9825         var header = {
9826             tag: 'thead',
9827             cn : []
9828         };
9829         
9830         var cm = this.cm;
9831         this.totalWidth = 0;
9832         
9833         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9834             
9835             var config = cm.config[i];
9836             
9837             var c = {
9838                 tag: 'th',
9839                 cls : 'x-hcol-' + i,
9840                 style : '',
9841                 
9842                 html: cm.getColumnHeader(i)
9843             };
9844             
9845             var tooltip = cm.getColumnTooltip(i);
9846             if (tooltip) {
9847                 c.tooltip = tooltip;
9848             }
9849             
9850             
9851             var hh = '';
9852             
9853             if(typeof(config.sortable) != 'undefined' && config.sortable){
9854                 c.cls += ' sortable';
9855                 c.html = '<i class="fa"></i>' + c.html;
9856             }
9857             
9858             // could use BS4 hidden-..-down 
9859             
9860             if(typeof(config.lgHeader) != 'undefined'){
9861                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9862             }
9863             
9864             if(typeof(config.mdHeader) != 'undefined'){
9865                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9866             }
9867             
9868             if(typeof(config.smHeader) != 'undefined'){
9869                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9870             }
9871             
9872             if(typeof(config.xsHeader) != 'undefined'){
9873                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9874             }
9875             
9876             if(hh.length){
9877                 c.html = hh;
9878             }
9879             
9880             if(typeof(config.tooltip) != 'undefined'){
9881                 c.tooltip = config.tooltip;
9882             }
9883             
9884             if(typeof(config.colspan) != 'undefined'){
9885                 c.colspan = config.colspan;
9886             }
9887             
9888             // hidden is handled by CSS now
9889             
9890             if(typeof(config.dataIndex) != 'undefined'){
9891                 c.sort = config.dataIndex;
9892             }
9893             
9894            
9895             
9896             if(typeof(config.align) != 'undefined' && config.align.length){
9897                 c.style += ' text-align:' + config.align + ';';
9898             }
9899             
9900             /* width is done in CSS
9901              *if(typeof(config.width) != 'undefined'){
9902                 c.style += ' width:' + config.width + 'px;';
9903                 this.totalWidth += config.width;
9904             } else {
9905                 this.totalWidth += 100; // assume minimum of 100 per column?
9906             }
9907             */
9908             
9909             if(typeof(config.cls) != 'undefined'){
9910                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9911             }
9912             // this is the bit that doesnt reall work at all...
9913             
9914             if (this.responsive) {
9915                  
9916             
9917                 ['xs','sm','md','lg'].map(function(size){
9918                     
9919                     if(typeof(config[size]) == 'undefined'){
9920                         return;
9921                     }
9922                      
9923                     if (!config[size]) { // 0 = hidden
9924                         // BS 4 '0' is treated as hide that column and below.
9925                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9926                         return;
9927                     }
9928                     
9929                     c.cls += ' col-' + size + '-' + config[size] + (
9930                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9931                     );
9932                     
9933                     
9934                 });
9935             }
9936             // at the end?
9937             
9938             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9939             
9940             
9941             
9942             
9943             header.cn.push(c)
9944         }
9945         
9946         return header;
9947     },
9948     
9949     renderBody : function()
9950     {
9951         var body = {
9952             tag: 'tbody',
9953             cn : [
9954                 {
9955                     tag: 'tr',
9956                     cn : [
9957                         {
9958                             tag : 'td',
9959                             colspan :  this.cm.getColumnCount()
9960                         }
9961                     ]
9962                 }
9963             ]
9964         };
9965         
9966         return body;
9967     },
9968     
9969     renderFooter : function()
9970     {
9971         var footer = {
9972             tag: 'tfoot',
9973             cn : [
9974                 {
9975                     tag: 'tr',
9976                     cn : [
9977                         {
9978                             tag : 'td',
9979                             colspan :  this.cm.getColumnCount()
9980                         }
9981                     ]
9982                 }
9983             ]
9984         };
9985         
9986         return footer;
9987     },
9988
9989     renderSummaryFooter : function()
9990     {
9991         var footer = {
9992             tag: 'tfoot',
9993             cn : []
9994         };
9995         
9996         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9997             
9998             var c = {
9999                 tag: 'td',
10000                 cls : 'x-fcol-' + i,
10001                 style : '',
10002                 html: ''
10003             };
10004             
10005             footer.cn.push(c)
10006         }
10007         
10008         return footer;
10009     },
10010     
10011     
10012     
10013     onLoad : function()
10014     {
10015 //        Roo.log('ds onload');
10016         this.clear();
10017         
10018         var _this = this;
10019         var cm = this.cm;
10020         var ds = this.store;
10021         
10022         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10023             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
10024             if (_this.store.sortInfo) {
10025                     
10026                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
10027                     e.select('i', true).addClass(['fa-arrow-up']);
10028                 }
10029                 
10030                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10031                     e.select('i', true).addClass(['fa-arrow-down']);
10032                 }
10033             }
10034         });
10035         
10036         var tbody =  this.bodyEl;
10037               
10038         if(ds.getCount() > 0){
10039             ds.data.each(function(d,rowIndex){
10040                 var row =  this.renderRow(cm, ds, rowIndex);
10041                 
10042                 tbody.createChild(row);
10043                 
10044                 var _this = this;
10045                 
10046                 if(row.cellObjects.length){
10047                     Roo.each(row.cellObjects, function(r){
10048                         _this.renderCellObject(r);
10049                     })
10050                 }
10051                 
10052             }, this);
10053         } else if (this.empty_results.length) {
10054             this.el.mask(this.empty_results, 'no-spinner');
10055         }
10056         
10057         var tfoot = this.el.select('tfoot', true).first();
10058         
10059         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10060             
10061             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10062             
10063             var total = this.ds.getTotalCount();
10064             
10065             if(this.footer.pageSize < total){
10066                 this.mainFoot.show();
10067             }
10068         }
10069
10070         if(!this.footerShow && this.summaryFooterShow) {
10071
10072             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10073         
10074                 var value = cm.config[i].summaryFooter;
10075
10076                 Roo.log('value [' + i + '] : ' + value);
10077             }
10078         }
10079         
10080         Roo.each(this.el.select('tbody td', true).elements, function(e){
10081             e.on('mouseover', _this.onMouseover, _this);
10082         });
10083         
10084         Roo.each(this.el.select('tbody td', true).elements, function(e){
10085             e.on('mouseout', _this.onMouseout, _this);
10086         });
10087         this.fireEvent('rowsrendered', this);
10088         
10089         this.autoSize();
10090         
10091         this.initCSS(); /// resize cols
10092
10093         
10094     },
10095     
10096     
10097     onUpdate : function(ds,record)
10098     {
10099         this.refreshRow(record);
10100         this.autoSize();
10101     },
10102     
10103     onRemove : function(ds, record, index, isUpdate){
10104         if(isUpdate !== true){
10105             this.fireEvent("beforerowremoved", this, index, record);
10106         }
10107         var bt = this.bodyEl.dom;
10108         
10109         var rows = this.el.select('tbody > tr', true).elements;
10110         
10111         if(typeof(rows[index]) != 'undefined'){
10112             bt.removeChild(rows[index].dom);
10113         }
10114         
10115 //        if(bt.rows[index]){
10116 //            bt.removeChild(bt.rows[index]);
10117 //        }
10118         
10119         if(isUpdate !== true){
10120             //this.stripeRows(index);
10121             //this.syncRowHeights(index, index);
10122             //this.layout();
10123             this.fireEvent("rowremoved", this, index, record);
10124         }
10125     },
10126     
10127     onAdd : function(ds, records, rowIndex)
10128     {
10129         //Roo.log('on Add called');
10130         // - note this does not handle multiple adding very well..
10131         var bt = this.bodyEl.dom;
10132         for (var i =0 ; i < records.length;i++) {
10133             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10134             //Roo.log(records[i]);
10135             //Roo.log(this.store.getAt(rowIndex+i));
10136             this.insertRow(this.store, rowIndex + i, false);
10137             return;
10138         }
10139         
10140     },
10141     
10142     
10143     refreshRow : function(record){
10144         var ds = this.store, index;
10145         if(typeof record == 'number'){
10146             index = record;
10147             record = ds.getAt(index);
10148         }else{
10149             index = ds.indexOf(record);
10150             if (index < 0) {
10151                 return; // should not happen - but seems to 
10152             }
10153         }
10154         this.insertRow(ds, index, true);
10155         this.autoSize();
10156         this.onRemove(ds, record, index+1, true);
10157         this.autoSize();
10158         //this.syncRowHeights(index, index);
10159         //this.layout();
10160         this.fireEvent("rowupdated", this, index, record);
10161     },
10162     // private - called by RowSelection
10163     onRowSelect : function(rowIndex){
10164         var row = this.getRowDom(rowIndex);
10165         row.addClass(['bg-info','info']);
10166     },
10167     // private - called by RowSelection
10168     onRowDeselect : function(rowIndex)
10169     {
10170         if (rowIndex < 0) {
10171             return;
10172         }
10173         var row = this.getRowDom(rowIndex);
10174         row.removeClass(['bg-info','info']);
10175     },
10176       /**
10177      * Focuses the specified row.
10178      * @param {Number} row The row index
10179      */
10180     focusRow : function(row)
10181     {
10182         //Roo.log('GridView.focusRow');
10183         var x = this.bodyEl.dom.scrollLeft;
10184         this.focusCell(row, 0, false);
10185         this.bodyEl.dom.scrollLeft = x;
10186
10187     },
10188      /**
10189      * Focuses the specified cell.
10190      * @param {Number} row The row index
10191      * @param {Number} col The column index
10192      * @param {Boolean} hscroll false to disable horizontal scrolling
10193      */
10194     focusCell : function(row, col, hscroll)
10195     {
10196         //Roo.log('GridView.focusCell');
10197         var el = this.ensureVisible(row, col, hscroll);
10198         // not sure what focusEL achives = it's a <a> pos relative 
10199         //this.focusEl.alignTo(el, "tl-tl");
10200         //if(Roo.isGecko){
10201         //    this.focusEl.focus();
10202         //}else{
10203         //    this.focusEl.focus.defer(1, this.focusEl);
10204         //}
10205     },
10206     
10207      /**
10208      * Scrolls the specified cell into view
10209      * @param {Number} row The row index
10210      * @param {Number} col The column index
10211      * @param {Boolean} hscroll false to disable horizontal scrolling
10212      */
10213     ensureVisible : function(row, col, hscroll)
10214     {
10215         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10216         //return null; //disable for testing.
10217         if(typeof row != "number"){
10218             row = row.rowIndex;
10219         }
10220         if(row < 0 && row >= this.ds.getCount()){
10221             return  null;
10222         }
10223         col = (col !== undefined ? col : 0);
10224         var cm = this.cm;
10225         while(cm.isHidden(col)){
10226             col++;
10227         }
10228
10229         var el = this.getCellDom(row, col);
10230         if(!el){
10231             return null;
10232         }
10233         var c = this.bodyEl.dom;
10234
10235         var ctop = parseInt(el.offsetTop, 10);
10236         var cleft = parseInt(el.offsetLeft, 10);
10237         var cbot = ctop + el.offsetHeight;
10238         var cright = cleft + el.offsetWidth;
10239
10240         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10241         var ch = 0; //?? header is not withing the area?
10242         var stop = parseInt(c.scrollTop, 10);
10243         var sleft = parseInt(c.scrollLeft, 10);
10244         var sbot = stop + ch;
10245         var sright = sleft + c.clientWidth;
10246         /*
10247         Roo.log('GridView.ensureVisible:' +
10248                 ' ctop:' + ctop +
10249                 ' c.clientHeight:' + c.clientHeight +
10250                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10251                 ' stop:' + stop +
10252                 ' cbot:' + cbot +
10253                 ' sbot:' + sbot +
10254                 ' ch:' + ch  
10255                 );
10256         */
10257         if(ctop < stop){
10258             c.scrollTop = ctop;
10259             //Roo.log("set scrolltop to ctop DISABLE?");
10260         }else if(cbot > sbot){
10261             //Roo.log("set scrolltop to cbot-ch");
10262             c.scrollTop = cbot-ch;
10263         }
10264
10265         if(hscroll !== false){
10266             if(cleft < sleft){
10267                 c.scrollLeft = cleft;
10268             }else if(cright > sright){
10269                 c.scrollLeft = cright-c.clientWidth;
10270             }
10271         }
10272
10273         return el;
10274     },
10275     
10276     
10277     insertRow : function(dm, rowIndex, isUpdate){
10278         
10279         if(!isUpdate){
10280             this.fireEvent("beforerowsinserted", this, rowIndex);
10281         }
10282             //var s = this.getScrollState();
10283         var row = this.renderRow(this.cm, this.store, rowIndex);
10284         // insert before rowIndex..
10285         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10286         
10287         var _this = this;
10288                 
10289         if(row.cellObjects.length){
10290             Roo.each(row.cellObjects, function(r){
10291                 _this.renderCellObject(r);
10292             })
10293         }
10294             
10295         if(!isUpdate){
10296             this.fireEvent("rowsinserted", this, rowIndex);
10297             //this.syncRowHeights(firstRow, lastRow);
10298             //this.stripeRows(firstRow);
10299             //this.layout();
10300         }
10301         
10302     },
10303     
10304     
10305     getRowDom : function(rowIndex)
10306     {
10307         var rows = this.el.select('tbody > tr', true).elements;
10308         
10309         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10310         
10311     },
10312     getCellDom : function(rowIndex, colIndex)
10313     {
10314         var row = this.getRowDom(rowIndex);
10315         if (row === false) {
10316             return false;
10317         }
10318         var cols = row.select('td', true).elements;
10319         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10320         
10321     },
10322     
10323     // returns the object tree for a tr..
10324   
10325     
10326     renderRow : function(cm, ds, rowIndex) 
10327     {
10328         var d = ds.getAt(rowIndex);
10329         
10330         var row = {
10331             tag : 'tr',
10332             cls : 'x-row-' + rowIndex,
10333             cn : []
10334         };
10335             
10336         var cellObjects = [];
10337         
10338         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10339             var config = cm.config[i];
10340             
10341             var renderer = cm.getRenderer(i);
10342             var value = '';
10343             var id = false;
10344             
10345             if(typeof(renderer) !== 'undefined'){
10346                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10347             }
10348             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10349             // and are rendered into the cells after the row is rendered - using the id for the element.
10350             
10351             if(typeof(value) === 'object'){
10352                 id = Roo.id();
10353                 cellObjects.push({
10354                     container : id,
10355                     cfg : value 
10356                 })
10357             }
10358             
10359             var rowcfg = {
10360                 record: d,
10361                 rowIndex : rowIndex,
10362                 colIndex : i,
10363                 rowClass : ''
10364             };
10365
10366             this.fireEvent('rowclass', this, rowcfg);
10367             
10368             var td = {
10369                 tag: 'td',
10370                 // this might end up displaying HTML?
10371                 // this is too messy... - better to only do it on columsn you know are going to be too long
10372                 //tooltip : (typeof(value) === 'object') ? '' : value,
10373                 cls : rowcfg.rowClass + ' x-col-' + i,
10374                 style: '',
10375                 html: (typeof(value) === 'object') ? '' : value
10376             };
10377             
10378             if (id) {
10379                 td.id = id;
10380             }
10381             
10382             if(typeof(config.colspan) != 'undefined'){
10383                 td.colspan = config.colspan;
10384             }
10385             
10386             
10387             
10388             if(typeof(config.align) != 'undefined' && config.align.length){
10389                 td.style += ' text-align:' + config.align + ';';
10390             }
10391             if(typeof(config.valign) != 'undefined' && config.valign.length){
10392                 td.style += ' vertical-align:' + config.valign + ';';
10393             }
10394             /*
10395             if(typeof(config.width) != 'undefined'){
10396                 td.style += ' width:' +  config.width + 'px;';
10397             }
10398             */
10399             
10400             if(typeof(config.cursor) != 'undefined'){
10401                 td.style += ' cursor:' +  config.cursor + ';';
10402             }
10403             
10404             if(typeof(config.cls) != 'undefined'){
10405                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10406             }
10407             if (this.responsive) {
10408                 ['xs','sm','md','lg'].map(function(size){
10409                     
10410                     if(typeof(config[size]) == 'undefined'){
10411                         return;
10412                     }
10413                     
10414                     
10415                       
10416                     if (!config[size]) { // 0 = hidden
10417                         // BS 4 '0' is treated as hide that column and below.
10418                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10419                         return;
10420                     }
10421                     
10422                     td.cls += ' col-' + size + '-' + config[size] + (
10423                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10424                     );
10425                      
10426     
10427                 });
10428             }
10429             row.cn.push(td);
10430            
10431         }
10432         
10433         row.cellObjects = cellObjects;
10434         
10435         return row;
10436           
10437     },
10438     
10439     
10440     
10441     onBeforeLoad : function()
10442     {
10443         this.el.unmask(); // if needed.
10444     },
10445      /**
10446      * Remove all rows
10447      */
10448     clear : function()
10449     {
10450         this.el.select('tbody', true).first().dom.innerHTML = '';
10451     },
10452     /**
10453      * Show or hide a row.
10454      * @param {Number} rowIndex to show or hide
10455      * @param {Boolean} state hide
10456      */
10457     setRowVisibility : function(rowIndex, state)
10458     {
10459         var bt = this.bodyEl.dom;
10460         
10461         var rows = this.el.select('tbody > tr', true).elements;
10462         
10463         if(typeof(rows[rowIndex]) == 'undefined'){
10464             return;
10465         }
10466         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10467         
10468     },
10469     
10470     
10471     getSelectionModel : function(){
10472         if(!this.selModel){
10473             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10474         }
10475         return this.selModel;
10476     },
10477     /*
10478      * Render the Roo.bootstrap object from renderder
10479      */
10480     renderCellObject : function(r)
10481     {
10482         var _this = this;
10483         
10484         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10485         
10486         var t = r.cfg.render(r.container);
10487         
10488         if(r.cfg.cn){
10489             Roo.each(r.cfg.cn, function(c){
10490                 var child = {
10491                     container: t.getChildContainer(),
10492                     cfg: c
10493                 };
10494                 _this.renderCellObject(child);
10495             })
10496         }
10497     },
10498     /**
10499      * get the Row Index from a dom element.
10500      * @param {Roo.Element} row The row to look for
10501      * @returns {Number} the row
10502      */
10503     getRowIndex : function(row)
10504     {
10505         var rowIndex = -1;
10506         
10507         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10508             if(el != row){
10509                 return;
10510             }
10511             
10512             rowIndex = index;
10513         });
10514         
10515         return rowIndex;
10516     },
10517     /**
10518      * get the header TH element for columnIndex
10519      * @param {Number} columnIndex
10520      * @returns {Roo.Element}
10521      */
10522     getHeaderIndex: function(colIndex)
10523     {
10524         var cols = this.headEl.select('th', true).elements;
10525         return cols[colIndex]; 
10526     },
10527     /**
10528      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10529      * @param {domElement} cell to look for
10530      * @returns {Number} the column
10531      */
10532     getCellIndex : function(cell)
10533     {
10534         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10535         if(id){
10536             return parseInt(id[1], 10);
10537         }
10538         return 0;
10539     },
10540      /**
10541      * Returns the grid's underlying element = used by panel.Grid
10542      * @return {Element} The element
10543      */
10544     getGridEl : function(){
10545         return this.el;
10546     },
10547      /**
10548      * Forces a resize - used by panel.Grid
10549      * @return {Element} The element
10550      */
10551     autoSize : function()
10552     {
10553         if(this.disableAutoSize) {
10554             return;
10555         }
10556         //var ctr = Roo.get(this.container.dom.parentElement);
10557         var ctr = Roo.get(this.el.dom);
10558         
10559         var thd = this.getGridEl().select('thead',true).first();
10560         var tbd = this.getGridEl().select('tbody', true).first();
10561         var tfd = this.getGridEl().select('tfoot', true).first();
10562         
10563         var cw = ctr.getWidth();
10564         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10565         
10566         if (tbd) {
10567             
10568             tbd.setWidth(ctr.getWidth());
10569             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10570             // this needs fixing for various usage - currently only hydra job advers I think..
10571             //tdb.setHeight(
10572             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10573             //); 
10574             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10575             cw -= barsize;
10576         }
10577         cw = Math.max(cw, this.totalWidth);
10578         this.getGridEl().select('tbody tr',true).setWidth(cw);
10579         this.initCSS();
10580         
10581         // resize 'expandable coloumn?
10582         
10583         return; // we doe not have a view in this design..
10584         
10585     },
10586     onBodyScroll: function()
10587     {
10588         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10589         if(this.headEl){
10590             this.headEl.setStyle({
10591                 'position' : 'relative',
10592                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10593             });
10594         }
10595         
10596         if(this.lazyLoad){
10597             
10598             var scrollHeight = this.bodyEl.dom.scrollHeight;
10599             
10600             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10601             
10602             var height = this.bodyEl.getHeight();
10603             
10604             if(scrollHeight - height == scrollTop) {
10605                 
10606                 var total = this.ds.getTotalCount();
10607                 
10608                 if(this.footer.cursor + this.footer.pageSize < total){
10609                     
10610                     this.footer.ds.load({
10611                         params : {
10612                             start : this.footer.cursor + this.footer.pageSize,
10613                             limit : this.footer.pageSize
10614                         },
10615                         add : true
10616                     });
10617                 }
10618             }
10619             
10620         }
10621     },
10622     onColumnSplitterMoved : function(i, diff)
10623     {
10624         this.userResized = true;
10625         
10626         var cm = this.colModel;
10627         
10628         var w = this.getHeaderIndex(i).getWidth() + diff;
10629         
10630         
10631         cm.setColumnWidth(i, w, true);
10632         this.initCSS();
10633         //var cid = cm.getColumnId(i); << not used in this version?
10634        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10635         
10636         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10637         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10638         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10639 */
10640         //this.updateSplitters();
10641         //this.layout(); << ??
10642         this.fireEvent("columnresize", i, w);
10643     },
10644     onHeaderChange : function()
10645     {
10646         var header = this.renderHeader();
10647         var table = this.el.select('table', true).first();
10648         
10649         this.headEl.remove();
10650         this.headEl = table.createChild(header, this.bodyEl, false);
10651         
10652         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10653             e.on('click', this.sort, this);
10654         }, this);
10655         
10656         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10657             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10658         }
10659         
10660     },
10661     
10662     onHiddenChange : function(colModel, colIndex, hidden)
10663     {
10664         /*
10665         this.cm.setHidden()
10666         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10667         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10668         
10669         this.CSS.updateRule(thSelector, "display", "");
10670         this.CSS.updateRule(tdSelector, "display", "");
10671         
10672         if(hidden){
10673             this.CSS.updateRule(thSelector, "display", "none");
10674             this.CSS.updateRule(tdSelector, "display", "none");
10675         }
10676         */
10677         // onload calls initCSS()
10678         this.onHeaderChange();
10679         this.onLoad();
10680     },
10681     
10682     setColumnWidth: function(col_index, width)
10683     {
10684         // width = "md-2 xs-2..."
10685         if(!this.colModel.config[col_index]) {
10686             return;
10687         }
10688         
10689         var w = width.split(" ");
10690         
10691         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10692         
10693         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10694         
10695         
10696         for(var j = 0; j < w.length; j++) {
10697             
10698             if(!w[j]) {
10699                 continue;
10700             }
10701             
10702             var size_cls = w[j].split("-");
10703             
10704             if(!Number.isInteger(size_cls[1] * 1)) {
10705                 continue;
10706             }
10707             
10708             if(!this.colModel.config[col_index][size_cls[0]]) {
10709                 continue;
10710             }
10711             
10712             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10713                 continue;
10714             }
10715             
10716             h_row[0].classList.replace(
10717                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10718                 "col-"+size_cls[0]+"-"+size_cls[1]
10719             );
10720             
10721             for(var i = 0; i < rows.length; i++) {
10722                 
10723                 var size_cls = w[j].split("-");
10724                 
10725                 if(!Number.isInteger(size_cls[1] * 1)) {
10726                     continue;
10727                 }
10728                 
10729                 if(!this.colModel.config[col_index][size_cls[0]]) {
10730                     continue;
10731                 }
10732                 
10733                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10734                     continue;
10735                 }
10736                 
10737                 rows[i].classList.replace(
10738                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10739                     "col-"+size_cls[0]+"-"+size_cls[1]
10740                 );
10741             }
10742             
10743             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10744         }
10745     }
10746 });
10747
10748 // currently only used to find the split on drag.. 
10749 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10750
10751 /**
10752  * @depricated
10753 */
10754 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10755 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10756 /*
10757  * - LGPL
10758  *
10759  * table cell
10760  * 
10761  */
10762
10763 /**
10764  * @class Roo.bootstrap.TableCell
10765  * @extends Roo.bootstrap.Component
10766  * @children Roo.bootstrap.Component
10767  * @parent Roo.bootstrap.TableRow
10768  * Bootstrap TableCell class
10769  * 
10770  * @cfg {String} html cell contain text
10771  * @cfg {String} cls cell class
10772  * @cfg {String} tag cell tag (td|th) default td
10773  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10774  * @cfg {String} align Aligns the content in a cell
10775  * @cfg {String} axis Categorizes cells
10776  * @cfg {String} bgcolor Specifies the background color of a cell
10777  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10778  * @cfg {Number} colspan Specifies the number of columns a cell should span
10779  * @cfg {String} headers Specifies one or more header cells a cell is related to
10780  * @cfg {Number} height Sets the height of a cell
10781  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10782  * @cfg {Number} rowspan Sets the number of rows a cell should span
10783  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10784  * @cfg {String} valign Vertical aligns the content in a cell
10785  * @cfg {Number} width Specifies the width of a cell
10786  * 
10787  * @constructor
10788  * Create a new TableCell
10789  * @param {Object} config The config object
10790  */
10791
10792 Roo.bootstrap.TableCell = function(config){
10793     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10794 };
10795
10796 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10797     
10798     html: false,
10799     cls: false,
10800     tag: false,
10801     abbr: false,
10802     align: false,
10803     axis: false,
10804     bgcolor: false,
10805     charoff: false,
10806     colspan: false,
10807     headers: false,
10808     height: false,
10809     nowrap: false,
10810     rowspan: false,
10811     scope: false,
10812     valign: false,
10813     width: false,
10814     
10815     
10816     getAutoCreate : function(){
10817         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10818         
10819         cfg = {
10820             tag: 'td'
10821         };
10822         
10823         if(this.tag){
10824             cfg.tag = this.tag;
10825         }
10826         
10827         if (this.html) {
10828             cfg.html=this.html
10829         }
10830         if (this.cls) {
10831             cfg.cls=this.cls
10832         }
10833         if (this.abbr) {
10834             cfg.abbr=this.abbr
10835         }
10836         if (this.align) {
10837             cfg.align=this.align
10838         }
10839         if (this.axis) {
10840             cfg.axis=this.axis
10841         }
10842         if (this.bgcolor) {
10843             cfg.bgcolor=this.bgcolor
10844         }
10845         if (this.charoff) {
10846             cfg.charoff=this.charoff
10847         }
10848         if (this.colspan) {
10849             cfg.colspan=this.colspan
10850         }
10851         if (this.headers) {
10852             cfg.headers=this.headers
10853         }
10854         if (this.height) {
10855             cfg.height=this.height
10856         }
10857         if (this.nowrap) {
10858             cfg.nowrap=this.nowrap
10859         }
10860         if (this.rowspan) {
10861             cfg.rowspan=this.rowspan
10862         }
10863         if (this.scope) {
10864             cfg.scope=this.scope
10865         }
10866         if (this.valign) {
10867             cfg.valign=this.valign
10868         }
10869         if (this.width) {
10870             cfg.width=this.width
10871         }
10872         
10873         
10874         return cfg;
10875     }
10876    
10877 });
10878
10879  
10880
10881  /*
10882  * - LGPL
10883  *
10884  * table row
10885  * 
10886  */
10887
10888 /**
10889  * @class Roo.bootstrap.TableRow
10890  * @extends Roo.bootstrap.Component
10891  * @children Roo.bootstrap.TableCell
10892  * @parent Roo.bootstrap.TableBody
10893  * Bootstrap TableRow class
10894  * @cfg {String} cls row class
10895  * @cfg {String} align Aligns the content in a table row
10896  * @cfg {String} bgcolor Specifies a background color for a table row
10897  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10898  * @cfg {String} valign Vertical aligns the content in a table row
10899  * 
10900  * @constructor
10901  * Create a new TableRow
10902  * @param {Object} config The config object
10903  */
10904
10905 Roo.bootstrap.TableRow = function(config){
10906     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10907 };
10908
10909 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10910     
10911     cls: false,
10912     align: false,
10913     bgcolor: false,
10914     charoff: false,
10915     valign: false,
10916     
10917     getAutoCreate : function(){
10918         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10919         
10920         cfg = {
10921             tag: 'tr'
10922         };
10923             
10924         if(this.cls){
10925             cfg.cls = this.cls;
10926         }
10927         if(this.align){
10928             cfg.align = this.align;
10929         }
10930         if(this.bgcolor){
10931             cfg.bgcolor = this.bgcolor;
10932         }
10933         if(this.charoff){
10934             cfg.charoff = this.charoff;
10935         }
10936         if(this.valign){
10937             cfg.valign = this.valign;
10938         }
10939         
10940         return cfg;
10941     }
10942    
10943 });
10944
10945  
10946
10947  /*
10948  * - LGPL
10949  *
10950  * table body
10951  * 
10952  */
10953
10954 /**
10955  * @class Roo.bootstrap.TableBody
10956  * @extends Roo.bootstrap.Component
10957  * @children Roo.bootstrap.TableRow
10958  * @parent Roo.bootstrap.Table
10959  * Bootstrap TableBody class
10960  * @cfg {String} cls element class
10961  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10962  * @cfg {String} align Aligns the content inside the element
10963  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10964  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10965  * 
10966  * @constructor
10967  * Create a new TableBody
10968  * @param {Object} config The config object
10969  */
10970
10971 Roo.bootstrap.TableBody = function(config){
10972     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10973 };
10974
10975 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10976     
10977     cls: false,
10978     tag: false,
10979     align: false,
10980     charoff: false,
10981     valign: false,
10982     
10983     getAutoCreate : function(){
10984         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10985         
10986         cfg = {
10987             tag: 'tbody'
10988         };
10989             
10990         if (this.cls) {
10991             cfg.cls=this.cls
10992         }
10993         if(this.tag){
10994             cfg.tag = this.tag;
10995         }
10996         
10997         if(this.align){
10998             cfg.align = this.align;
10999         }
11000         if(this.charoff){
11001             cfg.charoff = this.charoff;
11002         }
11003         if(this.valign){
11004             cfg.valign = this.valign;
11005         }
11006         
11007         return cfg;
11008     }
11009     
11010     
11011 //    initEvents : function()
11012 //    {
11013 //        
11014 //        if(!this.store){
11015 //            return;
11016 //        }
11017 //        
11018 //        this.store = Roo.factory(this.store, Roo.data);
11019 //        this.store.on('load', this.onLoad, this);
11020 //        
11021 //        this.store.load();
11022 //        
11023 //    },
11024 //    
11025 //    onLoad: function () 
11026 //    {   
11027 //        this.fireEvent('load', this);
11028 //    }
11029 //    
11030 //   
11031 });
11032
11033  
11034
11035  /*
11036  * Based on:
11037  * Ext JS Library 1.1.1
11038  * Copyright(c) 2006-2007, Ext JS, LLC.
11039  *
11040  * Originally Released Under LGPL - original licence link has changed is not relivant.
11041  *
11042  * Fork - LGPL
11043  * <script type="text/javascript">
11044  */
11045
11046 // as we use this in bootstrap.
11047 Roo.namespace('Roo.form');
11048  /**
11049  * @class Roo.form.Action
11050  * Internal Class used to handle form actions
11051  * @constructor
11052  * @param {Roo.form.BasicForm} el The form element or its id
11053  * @param {Object} config Configuration options
11054  */
11055
11056  
11057  
11058 // define the action interface
11059 Roo.form.Action = function(form, options){
11060     this.form = form;
11061     this.options = options || {};
11062 };
11063 /**
11064  * Client Validation Failed
11065  * @const 
11066  */
11067 Roo.form.Action.CLIENT_INVALID = 'client';
11068 /**
11069  * Server Validation Failed
11070  * @const 
11071  */
11072 Roo.form.Action.SERVER_INVALID = 'server';
11073  /**
11074  * Connect to Server Failed
11075  * @const 
11076  */
11077 Roo.form.Action.CONNECT_FAILURE = 'connect';
11078 /**
11079  * Reading Data from Server Failed
11080  * @const 
11081  */
11082 Roo.form.Action.LOAD_FAILURE = 'load';
11083
11084 Roo.form.Action.prototype = {
11085     type : 'default',
11086     failureType : undefined,
11087     response : undefined,
11088     result : undefined,
11089
11090     // interface method
11091     run : function(options){
11092
11093     },
11094
11095     // interface method
11096     success : function(response){
11097
11098     },
11099
11100     // interface method
11101     handleResponse : function(response){
11102
11103     },
11104
11105     // default connection failure
11106     failure : function(response){
11107         
11108         this.response = response;
11109         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11110         this.form.afterAction(this, false);
11111     },
11112
11113     processResponse : function(response){
11114         this.response = response;
11115         if(!response.responseText){
11116             return true;
11117         }
11118         this.result = this.handleResponse(response);
11119         return this.result;
11120     },
11121
11122     // utility functions used internally
11123     getUrl : function(appendParams){
11124         var url = this.options.url || this.form.url || this.form.el.dom.action;
11125         if(appendParams){
11126             var p = this.getParams();
11127             if(p){
11128                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11129             }
11130         }
11131         return url;
11132     },
11133
11134     getMethod : function(){
11135         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11136     },
11137
11138     getParams : function(){
11139         var bp = this.form.baseParams;
11140         var p = this.options.params;
11141         if(p){
11142             if(typeof p == "object"){
11143                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11144             }else if(typeof p == 'string' && bp){
11145                 p += '&' + Roo.urlEncode(bp);
11146             }
11147         }else if(bp){
11148             p = Roo.urlEncode(bp);
11149         }
11150         return p;
11151     },
11152
11153     createCallback : function(){
11154         return {
11155             success: this.success,
11156             failure: this.failure,
11157             scope: this,
11158             timeout: (this.form.timeout*1000),
11159             upload: this.form.fileUpload ? this.success : undefined
11160         };
11161     }
11162 };
11163
11164 Roo.form.Action.Submit = function(form, options){
11165     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11166 };
11167
11168 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11169     type : 'submit',
11170
11171     haveProgress : false,
11172     uploadComplete : false,
11173     
11174     // uploadProgress indicator.
11175     uploadProgress : function()
11176     {
11177         if (!this.form.progressUrl) {
11178             return;
11179         }
11180         
11181         if (!this.haveProgress) {
11182             Roo.MessageBox.progress("Uploading", "Uploading");
11183         }
11184         if (this.uploadComplete) {
11185            Roo.MessageBox.hide();
11186            return;
11187         }
11188         
11189         this.haveProgress = true;
11190    
11191         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11192         
11193         var c = new Roo.data.Connection();
11194         c.request({
11195             url : this.form.progressUrl,
11196             params: {
11197                 id : uid
11198             },
11199             method: 'GET',
11200             success : function(req){
11201                //console.log(data);
11202                 var rdata = false;
11203                 var edata;
11204                 try  {
11205                    rdata = Roo.decode(req.responseText)
11206                 } catch (e) {
11207                     Roo.log("Invalid data from server..");
11208                     Roo.log(edata);
11209                     return;
11210                 }
11211                 if (!rdata || !rdata.success) {
11212                     Roo.log(rdata);
11213                     Roo.MessageBox.alert(Roo.encode(rdata));
11214                     return;
11215                 }
11216                 var data = rdata.data;
11217                 
11218                 if (this.uploadComplete) {
11219                    Roo.MessageBox.hide();
11220                    return;
11221                 }
11222                    
11223                 if (data){
11224                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11225                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11226                     );
11227                 }
11228                 this.uploadProgress.defer(2000,this);
11229             },
11230        
11231             failure: function(data) {
11232                 Roo.log('progress url failed ');
11233                 Roo.log(data);
11234             },
11235             scope : this
11236         });
11237            
11238     },
11239     
11240     
11241     run : function()
11242     {
11243         // run get Values on the form, so it syncs any secondary forms.
11244         this.form.getValues();
11245         
11246         var o = this.options;
11247         var method = this.getMethod();
11248         var isPost = method == 'POST';
11249         if(o.clientValidation === false || this.form.isValid()){
11250             
11251             if (this.form.progressUrl) {
11252                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11253                     (new Date() * 1) + '' + Math.random());
11254                     
11255             } 
11256             
11257             
11258             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11259                 form:this.form.el.dom,
11260                 url:this.getUrl(!isPost),
11261                 method: method,
11262                 params:isPost ? this.getParams() : null,
11263                 isUpload: this.form.fileUpload,
11264                 formData : this.form.formData
11265             }));
11266             
11267             this.uploadProgress();
11268
11269         }else if (o.clientValidation !== false){ // client validation failed
11270             this.failureType = Roo.form.Action.CLIENT_INVALID;
11271             this.form.afterAction(this, false);
11272         }
11273     },
11274
11275     success : function(response)
11276     {
11277         this.uploadComplete= true;
11278         if (this.haveProgress) {
11279             Roo.MessageBox.hide();
11280         }
11281         
11282         
11283         var result = this.processResponse(response);
11284         if(result === true || result.success){
11285             this.form.afterAction(this, true);
11286             return;
11287         }
11288         if(result.errors){
11289             this.form.markInvalid(result.errors);
11290             this.failureType = Roo.form.Action.SERVER_INVALID;
11291         }
11292         this.form.afterAction(this, false);
11293     },
11294     failure : function(response)
11295     {
11296         this.uploadComplete= true;
11297         if (this.haveProgress) {
11298             Roo.MessageBox.hide();
11299         }
11300         
11301         this.response = response;
11302         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11303         this.form.afterAction(this, false);
11304     },
11305     
11306     handleResponse : function(response){
11307         if(this.form.errorReader){
11308             var rs = this.form.errorReader.read(response);
11309             var errors = [];
11310             if(rs.records){
11311                 for(var i = 0, len = rs.records.length; i < len; i++) {
11312                     var r = rs.records[i];
11313                     errors[i] = r.data;
11314                 }
11315             }
11316             if(errors.length < 1){
11317                 errors = null;
11318             }
11319             return {
11320                 success : rs.success,
11321                 errors : errors
11322             };
11323         }
11324         var ret = false;
11325         try {
11326             ret = Roo.decode(response.responseText);
11327         } catch (e) {
11328             ret = {
11329                 success: false,
11330                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11331                 errors : []
11332             };
11333         }
11334         return ret;
11335         
11336     }
11337 });
11338
11339
11340 Roo.form.Action.Load = function(form, options){
11341     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11342     this.reader = this.form.reader;
11343 };
11344
11345 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11346     type : 'load',
11347
11348     run : function(){
11349         
11350         Roo.Ajax.request(Roo.apply(
11351                 this.createCallback(), {
11352                     method:this.getMethod(),
11353                     url:this.getUrl(false),
11354                     params:this.getParams()
11355         }));
11356     },
11357
11358     success : function(response){
11359         
11360         var result = this.processResponse(response);
11361         if(result === true || !result.success || !result.data){
11362             this.failureType = Roo.form.Action.LOAD_FAILURE;
11363             this.form.afterAction(this, false);
11364             return;
11365         }
11366         this.form.clearInvalid();
11367         this.form.setValues(result.data);
11368         this.form.afterAction(this, true);
11369     },
11370
11371     handleResponse : function(response){
11372         if(this.form.reader){
11373             var rs = this.form.reader.read(response);
11374             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11375             return {
11376                 success : rs.success,
11377                 data : data
11378             };
11379         }
11380         return Roo.decode(response.responseText);
11381     }
11382 });
11383
11384 Roo.form.Action.ACTION_TYPES = {
11385     'load' : Roo.form.Action.Load,
11386     'submit' : Roo.form.Action.Submit
11387 };/*
11388  * - LGPL
11389  *
11390  * form
11391  *
11392  */
11393
11394 /**
11395  * @class Roo.bootstrap.form.Form
11396  * @extends Roo.bootstrap.Component
11397  * @children Roo.bootstrap.Component
11398  * Bootstrap Form class
11399  * @cfg {String} method  GET | POST (default POST)
11400  * @cfg {String} labelAlign top | left (default top)
11401  * @cfg {String} align left  | right - for navbars
11402  * @cfg {Boolean} loadMask load mask when submit (default true)
11403
11404  *
11405  * @constructor
11406  * Create a new Form
11407  * @param {Object} config The config object
11408  */
11409
11410
11411 Roo.bootstrap.form.Form = function(config){
11412     
11413     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11414     
11415     Roo.bootstrap.form.Form.popover.apply();
11416     
11417     this.addEvents({
11418         /**
11419          * @event clientvalidation
11420          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11421          * @param {Form} this
11422          * @param {Boolean} valid true if the form has passed client-side validation
11423          */
11424         clientvalidation: true,
11425         /**
11426          * @event beforeaction
11427          * Fires before any action is performed. Return false to cancel the action.
11428          * @param {Form} this
11429          * @param {Action} action The action to be performed
11430          */
11431         beforeaction: true,
11432         /**
11433          * @event actionfailed
11434          * Fires when an action fails.
11435          * @param {Form} this
11436          * @param {Action} action The action that failed
11437          */
11438         actionfailed : true,
11439         /**
11440          * @event actioncomplete
11441          * Fires when an action is completed.
11442          * @param {Form} this
11443          * @param {Action} action The action that completed
11444          */
11445         actioncomplete : true
11446     });
11447 };
11448
11449 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11450
11451      /**
11452      * @cfg {String} method
11453      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11454      */
11455     method : 'POST',
11456     /**
11457      * @cfg {String} url
11458      * The URL to use for form actions if one isn't supplied in the action options.
11459      */
11460     /**
11461      * @cfg {Boolean} fileUpload
11462      * Set to true if this form is a file upload.
11463      */
11464
11465     /**
11466      * @cfg {Object} baseParams
11467      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11468      */
11469
11470     /**
11471      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11472      */
11473     timeout: 30,
11474     /**
11475      * @cfg {Sting} align (left|right) for navbar forms
11476      */
11477     align : 'left',
11478
11479     // private
11480     activeAction : null,
11481
11482     /**
11483      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11484      * element by passing it or its id or mask the form itself by passing in true.
11485      * @type Mixed
11486      */
11487     waitMsgTarget : false,
11488
11489     loadMask : true,
11490     
11491     /**
11492      * @cfg {Boolean} errorMask (true|false) default false
11493      */
11494     errorMask : false,
11495     
11496     /**
11497      * @cfg {Number} maskOffset Default 100
11498      */
11499     maskOffset : 100,
11500     
11501     /**
11502      * @cfg {Boolean} maskBody
11503      */
11504     maskBody : false,
11505
11506     getAutoCreate : function(){
11507
11508         var cfg = {
11509             tag: 'form',
11510             method : this.method || 'POST',
11511             id : this.id || Roo.id(),
11512             cls : ''
11513         };
11514         if (this.parent().xtype.match(/^Nav/)) {
11515             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11516
11517         }
11518
11519         if (this.labelAlign == 'left' ) {
11520             cfg.cls += ' form-horizontal';
11521         }
11522
11523
11524         return cfg;
11525     },
11526     initEvents : function()
11527     {
11528         this.el.on('submit', this.onSubmit, this);
11529         // this was added as random key presses on the form where triggering form submit.
11530         this.el.on('keypress', function(e) {
11531             if (e.getCharCode() != 13) {
11532                 return true;
11533             }
11534             // we might need to allow it for textareas.. and some other items.
11535             // check e.getTarget().
11536
11537             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11538                 return true;
11539             }
11540
11541             Roo.log("keypress blocked");
11542
11543             e.preventDefault();
11544             return false;
11545         });
11546         
11547     },
11548     // private
11549     onSubmit : function(e){
11550         e.stopEvent();
11551     },
11552
11553      /**
11554      * Returns true if client-side validation on the form is successful.
11555      * @return Boolean
11556      */
11557     isValid : function(){
11558         var items = this.getItems();
11559         var valid = true;
11560         var target = false;
11561         
11562         items.each(function(f){
11563             
11564             if(f.validate()){
11565                 return;
11566             }
11567             
11568             Roo.log('invalid field: ' + f.name);
11569             
11570             valid = false;
11571
11572             if(!target && f.el.isVisible(true)){
11573                 target = f;
11574             }
11575            
11576         });
11577         
11578         if(this.errorMask && !valid){
11579             Roo.bootstrap.form.Form.popover.mask(this, target);
11580         }
11581         
11582         return valid;
11583     },
11584     
11585     /**
11586      * Returns true if any fields in this form have changed since their original load.
11587      * @return Boolean
11588      */
11589     isDirty : function(){
11590         var dirty = false;
11591         var items = this.getItems();
11592         items.each(function(f){
11593            if(f.isDirty()){
11594                dirty = true;
11595                return false;
11596            }
11597            return true;
11598         });
11599         return dirty;
11600     },
11601      /**
11602      * Performs a predefined action (submit or load) or custom actions you define on this form.
11603      * @param {String} actionName The name of the action type
11604      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11605      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11606      * accept other config options):
11607      * <pre>
11608 Property          Type             Description
11609 ----------------  ---------------  ----------------------------------------------------------------------------------
11610 url               String           The url for the action (defaults to the form's url)
11611 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11612 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11613 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11614                                    validate the form on the client (defaults to false)
11615      * </pre>
11616      * @return {BasicForm} this
11617      */
11618     doAction : function(action, options){
11619         if(typeof action == 'string'){
11620             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11621         }
11622         if(this.fireEvent('beforeaction', this, action) !== false){
11623             this.beforeAction(action);
11624             action.run.defer(100, action);
11625         }
11626         return this;
11627     },
11628
11629     // private
11630     beforeAction : function(action){
11631         var o = action.options;
11632         
11633         if(this.loadMask){
11634             
11635             if(this.maskBody){
11636                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11637             } else {
11638                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11639             }
11640         }
11641         // not really supported yet.. ??
11642
11643         //if(this.waitMsgTarget === true){
11644         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11645         //}else if(this.waitMsgTarget){
11646         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11647         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11648         //}else {
11649         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11650        // }
11651
11652     },
11653
11654     // private
11655     afterAction : function(action, success){
11656         this.activeAction = null;
11657         var o = action.options;
11658
11659         if(this.loadMask){
11660             
11661             if(this.maskBody){
11662                 Roo.get(document.body).unmask();
11663             } else {
11664                 this.el.unmask();
11665             }
11666         }
11667         
11668         //if(this.waitMsgTarget === true){
11669 //            this.el.unmask();
11670         //}else if(this.waitMsgTarget){
11671         //    this.waitMsgTarget.unmask();
11672         //}else{
11673         //    Roo.MessageBox.updateProgress(1);
11674         //    Roo.MessageBox.hide();
11675        // }
11676         //
11677         if(success){
11678             if(o.reset){
11679                 this.reset();
11680             }
11681             Roo.callback(o.success, o.scope, [this, action]);
11682             this.fireEvent('actioncomplete', this, action);
11683
11684         }else{
11685
11686             // failure condition..
11687             // we have a scenario where updates need confirming.
11688             // eg. if a locking scenario exists..
11689             // we look for { errors : { needs_confirm : true }} in the response.
11690             if (
11691                 (typeof(action.result) != 'undefined')  &&
11692                 (typeof(action.result.errors) != 'undefined')  &&
11693                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11694            ){
11695                 var _t = this;
11696                 Roo.log("not supported yet");
11697                  /*
11698
11699                 Roo.MessageBox.confirm(
11700                     "Change requires confirmation",
11701                     action.result.errorMsg,
11702                     function(r) {
11703                         if (r != 'yes') {
11704                             return;
11705                         }
11706                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11707                     }
11708
11709                 );
11710                 */
11711
11712
11713                 return;
11714             }
11715
11716             Roo.callback(o.failure, o.scope, [this, action]);
11717             // show an error message if no failed handler is set..
11718             if (!this.hasListener('actionfailed')) {
11719                 Roo.log("need to add dialog support");
11720                 /*
11721                 Roo.MessageBox.alert("Error",
11722                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11723                         action.result.errorMsg :
11724                         "Saving Failed, please check your entries or try again"
11725                 );
11726                 */
11727             }
11728
11729             this.fireEvent('actionfailed', this, action);
11730         }
11731
11732     },
11733     /**
11734      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11735      * @param {String} id The value to search for
11736      * @return Field
11737      */
11738     findField : function(id){
11739         var items = this.getItems();
11740         var field = items.get(id);
11741         if(!field){
11742              items.each(function(f){
11743                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11744                     field = f;
11745                     return false;
11746                 }
11747                 return true;
11748             });
11749         }
11750         return field || null;
11751     },
11752      /**
11753      * Mark fields in this form invalid in bulk.
11754      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11755      * @return {BasicForm} this
11756      */
11757     markInvalid : function(errors){
11758         if(errors instanceof Array){
11759             for(var i = 0, len = errors.length; i < len; i++){
11760                 var fieldError = errors[i];
11761                 var f = this.findField(fieldError.id);
11762                 if(f){
11763                     f.markInvalid(fieldError.msg);
11764                 }
11765             }
11766         }else{
11767             var field, id;
11768             for(id in errors){
11769                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11770                     field.markInvalid(errors[id]);
11771                 }
11772             }
11773         }
11774         //Roo.each(this.childForms || [], function (f) {
11775         //    f.markInvalid(errors);
11776         //});
11777
11778         return this;
11779     },
11780
11781     /**
11782      * Set values for fields in this form in bulk.
11783      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11784      * @return {BasicForm} this
11785      */
11786     setValues : function(values){
11787         if(values instanceof Array){ // array of objects
11788             for(var i = 0, len = values.length; i < len; i++){
11789                 var v = values[i];
11790                 var f = this.findField(v.id);
11791                 if(f){
11792                     f.setValue(v.value);
11793                     if(this.trackResetOnLoad){
11794                         f.originalValue = f.getValue();
11795                     }
11796                 }
11797             }
11798         }else{ // object hash
11799             var field, id;
11800             for(id in values){
11801                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11802
11803                     if (field.setFromData &&
11804                         field.valueField &&
11805                         field.displayField &&
11806                         // combos' with local stores can
11807                         // be queried via setValue()
11808                         // to set their value..
11809                         (field.store && !field.store.isLocal)
11810                         ) {
11811                         // it's a combo
11812                         var sd = { };
11813                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11814                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11815                         field.setFromData(sd);
11816
11817                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11818                         
11819                         field.setFromData(values);
11820                         
11821                     } else {
11822                         field.setValue(values[id]);
11823                     }
11824
11825
11826                     if(this.trackResetOnLoad){
11827                         field.originalValue = field.getValue();
11828                     }
11829                 }
11830             }
11831         }
11832
11833         //Roo.each(this.childForms || [], function (f) {
11834         //    f.setValues(values);
11835         //});
11836
11837         return this;
11838     },
11839
11840     /**
11841      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11842      * they are returned as an array.
11843      * @param {Boolean} asString
11844      * @return {Object}
11845      */
11846     getValues : function(asString){
11847         //if (this.childForms) {
11848             // copy values from the child forms
11849         //    Roo.each(this.childForms, function (f) {
11850         //        this.setValues(f.getValues());
11851         //    }, this);
11852         //}
11853
11854
11855
11856         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11857         if(asString === true){
11858             return fs;
11859         }
11860         return Roo.urlDecode(fs);
11861     },
11862
11863     /**
11864      * Returns the fields in this form as an object with key/value pairs.
11865      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11866      * @return {Object}
11867      */
11868     getFieldValues : function(with_hidden)
11869     {
11870         var items = this.getItems();
11871         var ret = {};
11872         items.each(function(f){
11873             
11874             if (!f.getName()) {
11875                 return;
11876             }
11877             
11878             var v = f.getValue();
11879             
11880             if (f.inputType =='radio') {
11881                 if (typeof(ret[f.getName()]) == 'undefined') {
11882                     ret[f.getName()] = ''; // empty..
11883                 }
11884
11885                 if (!f.el.dom.checked) {
11886                     return;
11887
11888                 }
11889                 v = f.el.dom.value;
11890
11891             }
11892             
11893             if(f.xtype == 'MoneyField'){
11894                 ret[f.currencyName] = f.getCurrency();
11895             }
11896
11897             // not sure if this supported any more..
11898             if ((typeof(v) == 'object') && f.getRawValue) {
11899                 v = f.getRawValue() ; // dates..
11900             }
11901             // combo boxes where name != hiddenName...
11902             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11903                 ret[f.name] = f.getRawValue();
11904             }
11905             ret[f.getName()] = v;
11906         });
11907
11908         return ret;
11909     },
11910
11911     /**
11912      * Clears all invalid messages in this form.
11913      * @return {BasicForm} this
11914      */
11915     clearInvalid : function(){
11916         var items = this.getItems();
11917
11918         items.each(function(f){
11919            f.clearInvalid();
11920         });
11921
11922         return this;
11923     },
11924
11925     /**
11926      * Resets this form.
11927      * @return {BasicForm} this
11928      */
11929     reset : function(){
11930         var items = this.getItems();
11931         items.each(function(f){
11932             f.reset();
11933         });
11934
11935         Roo.each(this.childForms || [], function (f) {
11936             f.reset();
11937         });
11938
11939
11940         return this;
11941     },
11942     
11943     getItems : function()
11944     {
11945         var r=new Roo.util.MixedCollection(false, function(o){
11946             return o.id || (o.id = Roo.id());
11947         });
11948         var iter = function(el) {
11949             if (el.inputEl) {
11950                 r.add(el);
11951             }
11952             if (!el.items) {
11953                 return;
11954             }
11955             Roo.each(el.items,function(e) {
11956                 iter(e);
11957             });
11958         };
11959
11960         iter(this);
11961         return r;
11962     },
11963     
11964     hideFields : function(items)
11965     {
11966         Roo.each(items, function(i){
11967             
11968             var f = this.findField(i);
11969             
11970             if(!f){
11971                 return;
11972             }
11973             
11974             f.hide();
11975             
11976         }, this);
11977     },
11978     
11979     showFields : function(items)
11980     {
11981         Roo.each(items, function(i){
11982             
11983             var f = this.findField(i);
11984             
11985             if(!f){
11986                 return;
11987             }
11988             
11989             f.show();
11990             
11991         }, this);
11992     }
11993
11994 });
11995
11996 Roo.apply(Roo.bootstrap.form.Form, {
11997     
11998     popover : {
11999         
12000         padding : 5,
12001         
12002         isApplied : false,
12003         
12004         isMasked : false,
12005         
12006         form : false,
12007         
12008         target : false,
12009         
12010         toolTip : false,
12011         
12012         intervalID : false,
12013         
12014         maskEl : false,
12015         
12016         apply : function()
12017         {
12018             if(this.isApplied){
12019                 return;
12020             }
12021             
12022             this.maskEl = {
12023                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
12024                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
12025                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
12026                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
12027             };
12028             
12029             this.maskEl.top.enableDisplayMode("block");
12030             this.maskEl.left.enableDisplayMode("block");
12031             this.maskEl.bottom.enableDisplayMode("block");
12032             this.maskEl.right.enableDisplayMode("block");
12033             
12034             this.toolTip = new Roo.bootstrap.Tooltip({
12035                 cls : 'roo-form-error-popover',
12036                 alignment : {
12037                     'left' : ['r-l', [-2,0], 'right'],
12038                     'right' : ['l-r', [2,0], 'left'],
12039                     'bottom' : ['tl-bl', [0,2], 'top'],
12040                     'top' : [ 'bl-tl', [0,-2], 'bottom']
12041                 }
12042             });
12043             
12044             this.toolTip.render(Roo.get(document.body));
12045
12046             this.toolTip.el.enableDisplayMode("block");
12047             
12048             Roo.get(document.body).on('click', function(){
12049                 this.unmask();
12050             }, this);
12051             
12052             Roo.get(document.body).on('touchstart', function(){
12053                 this.unmask();
12054             }, this);
12055             
12056             this.isApplied = true
12057         },
12058         
12059         mask : function(form, target)
12060         {
12061             this.form = form;
12062             
12063             this.target = target;
12064             
12065             if(!this.form.errorMask || !target.el){
12066                 return;
12067             }
12068             
12069             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12070             
12071             Roo.log(scrollable);
12072             
12073             var ot = this.target.el.calcOffsetsTo(scrollable);
12074             
12075             var scrollTo = ot[1] - this.form.maskOffset;
12076             
12077             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12078             
12079             scrollable.scrollTo('top', scrollTo);
12080             
12081             var box = this.target.el.getBox();
12082             Roo.log(box);
12083             var zIndex = Roo.bootstrap.Modal.zIndex++;
12084
12085             
12086             this.maskEl.top.setStyle('position', 'absolute');
12087             this.maskEl.top.setStyle('z-index', zIndex);
12088             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12089             this.maskEl.top.setLeft(0);
12090             this.maskEl.top.setTop(0);
12091             this.maskEl.top.show();
12092             
12093             this.maskEl.left.setStyle('position', 'absolute');
12094             this.maskEl.left.setStyle('z-index', zIndex);
12095             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12096             this.maskEl.left.setLeft(0);
12097             this.maskEl.left.setTop(box.y - this.padding);
12098             this.maskEl.left.show();
12099
12100             this.maskEl.bottom.setStyle('position', 'absolute');
12101             this.maskEl.bottom.setStyle('z-index', zIndex);
12102             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12103             this.maskEl.bottom.setLeft(0);
12104             this.maskEl.bottom.setTop(box.bottom + this.padding);
12105             this.maskEl.bottom.show();
12106
12107             this.maskEl.right.setStyle('position', 'absolute');
12108             this.maskEl.right.setStyle('z-index', zIndex);
12109             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12110             this.maskEl.right.setLeft(box.right + this.padding);
12111             this.maskEl.right.setTop(box.y - this.padding);
12112             this.maskEl.right.show();
12113
12114             this.toolTip.bindEl = this.target.el;
12115
12116             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12117
12118             var tip = this.target.blankText;
12119
12120             if(this.target.getValue() !== '' ) {
12121                 
12122                 if (this.target.invalidText.length) {
12123                     tip = this.target.invalidText;
12124                 } else if (this.target.regexText.length){
12125                     tip = this.target.regexText;
12126                 }
12127             }
12128
12129             this.toolTip.show(tip);
12130
12131             this.intervalID = window.setInterval(function() {
12132                 Roo.bootstrap.form.Form.popover.unmask();
12133             }, 10000);
12134
12135             window.onwheel = function(){ return false;};
12136             
12137             (function(){ this.isMasked = true; }).defer(500, this);
12138             
12139         },
12140         
12141         unmask : function()
12142         {
12143             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12144                 return;
12145             }
12146             
12147             this.maskEl.top.setStyle('position', 'absolute');
12148             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12149             this.maskEl.top.hide();
12150
12151             this.maskEl.left.setStyle('position', 'absolute');
12152             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12153             this.maskEl.left.hide();
12154
12155             this.maskEl.bottom.setStyle('position', 'absolute');
12156             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12157             this.maskEl.bottom.hide();
12158
12159             this.maskEl.right.setStyle('position', 'absolute');
12160             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12161             this.maskEl.right.hide();
12162             
12163             this.toolTip.hide();
12164             
12165             this.toolTip.el.hide();
12166             
12167             window.onwheel = function(){ return true;};
12168             
12169             if(this.intervalID){
12170                 window.clearInterval(this.intervalID);
12171                 this.intervalID = false;
12172             }
12173             
12174             this.isMasked = false;
12175             
12176         }
12177         
12178     }
12179     
12180 });
12181
12182 /*
12183  * Based on:
12184  * Ext JS Library 1.1.1
12185  * Copyright(c) 2006-2007, Ext JS, LLC.
12186  *
12187  * Originally Released Under LGPL - original licence link has changed is not relivant.
12188  *
12189  * Fork - LGPL
12190  * <script type="text/javascript">
12191  */
12192 /**
12193  * @class Roo.form.VTypes
12194  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12195  * @static
12196  */
12197 Roo.form.VTypes = function(){
12198     // closure these in so they are only created once.
12199     var alpha = /^[a-zA-Z_]+$/;
12200     var alphanum = /^[a-zA-Z0-9_]+$/;
12201     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12202     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12203
12204     // All these messages and functions are configurable
12205     return {
12206         /**
12207          * The function used to validate email addresses
12208          * @param {String} value The email address
12209          */
12210         email : function(v){
12211             return email.test(v);
12212         },
12213         /**
12214          * The error text to display when the email validation function returns false
12215          * @type String
12216          */
12217         emailText : 'This field should be an e-mail address in the format "user@domain.com"',
12218         /**
12219          * The keystroke filter mask to be applied on email input
12220          * @type RegExp
12221          */
12222         emailMask : /[a-z0-9_\.\-@]/i,
12223
12224         /**
12225          * The function used to validate URLs
12226          * @param {String} value The URL
12227          */
12228         url : function(v){
12229             return url.test(v);
12230         },
12231         /**
12232          * The error text to display when the url validation function returns false
12233          * @type String
12234          */
12235         urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12236         
12237         /**
12238          * The function used to validate alpha values
12239          * @param {String} value The value
12240          */
12241         alpha : function(v){
12242             return alpha.test(v);
12243         },
12244         /**
12245          * The error text to display when the alpha validation function returns false
12246          * @type String
12247          */
12248         alphaText : 'This field should only contain letters and _',
12249         /**
12250          * The keystroke filter mask to be applied on alpha input
12251          * @type RegExp
12252          */
12253         alphaMask : /[a-z_]/i,
12254
12255         /**
12256          * The function used to validate alphanumeric values
12257          * @param {String} value The value
12258          */
12259         alphanum : function(v){
12260             return alphanum.test(v);
12261         },
12262         /**
12263          * The error text to display when the alphanumeric validation function returns false
12264          * @type String
12265          */
12266         alphanumText : 'This field should only contain letters, numbers and _',
12267         /**
12268          * The keystroke filter mask to be applied on alphanumeric input
12269          * @type RegExp
12270          */
12271         alphanumMask : /[a-z0-9_]/i
12272     };
12273 }();/*
12274  * - LGPL
12275  *
12276  * Input
12277  * 
12278  */
12279
12280 /**
12281  * @class Roo.bootstrap.form.Input
12282  * @extends Roo.bootstrap.Component
12283  * Bootstrap Input class
12284  * @cfg {Boolean} disabled is it disabled
12285  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12286  * @cfg {String} name name of the input
12287  * @cfg {string} fieldLabel - the label associated
12288  * @cfg {string} placeholder - placeholder to put in text.
12289  * @cfg {string} before - input group add on before
12290  * @cfg {string} after - input group add on after
12291  * @cfg {string} size - (lg|sm) or leave empty..
12292  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12293  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12294  * @cfg {Number} md colspan out of 12 for computer-sized screens
12295  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12296  * @cfg {string} value default value of the input
12297  * @cfg {Number} labelWidth set the width of label 
12298  * @cfg {Number} labellg set the width of label (1-12)
12299  * @cfg {Number} labelmd set the width of label (1-12)
12300  * @cfg {Number} labelsm set the width of label (1-12)
12301  * @cfg {Number} labelxs set the width of label (1-12)
12302  * @cfg {String} labelAlign (top|left)
12303  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12304  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12305  * @cfg {String} indicatorpos (left|right) default left
12306  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12307  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12308  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12309  * @cfg {Roo.bootstrap.Button} before Button to show before
12310  * @cfg {Roo.bootstrap.Button} afterButton to show before
12311  * @cfg {String} align (left|center|right) Default left
12312  * @cfg {Boolean} forceFeedback (true|false) Default false
12313  * 
12314  * @constructor
12315  * Create a new Input
12316  * @param {Object} config The config object
12317  */
12318
12319 Roo.bootstrap.form.Input = function(config){
12320     
12321     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12322     
12323     this.addEvents({
12324         /**
12325          * @event focus
12326          * Fires when this field receives input focus.
12327          * @param {Roo.form.Field} this
12328          */
12329         focus : true,
12330         /**
12331          * @event blur
12332          * Fires when this field loses input focus.
12333          * @param {Roo.form.Field} this
12334          */
12335         blur : true,
12336         /**
12337          * @event specialkey
12338          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12339          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12340          * @param {Roo.form.Field} this
12341          * @param {Roo.EventObject} e The event object
12342          */
12343         specialkey : true,
12344         /**
12345          * @event change
12346          * Fires just before the field blurs if the field value has changed.
12347          * @param {Roo.form.Field} this
12348          * @param {Mixed} newValue The new value
12349          * @param {Mixed} oldValue The original value
12350          */
12351         change : true,
12352         /**
12353          * @event invalid
12354          * Fires after the field has been marked as invalid.
12355          * @param {Roo.form.Field} this
12356          * @param {String} msg The validation message
12357          */
12358         invalid : true,
12359         /**
12360          * @event valid
12361          * Fires after the field has been validated with no errors.
12362          * @param {Roo.form.Field} this
12363          */
12364         valid : true,
12365          /**
12366          * @event keyup
12367          * Fires after the key up
12368          * @param {Roo.form.Field} this
12369          * @param {Roo.EventObject}  e The event Object
12370          */
12371         keyup : true,
12372         /**
12373          * @event paste
12374          * Fires after the user pastes into input
12375          * @param {Roo.form.Field} this
12376          * @param {Roo.EventObject}  e The event Object
12377          */
12378         paste : true
12379     });
12380 };
12381
12382 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12383      /**
12384      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12385       automatic validation (defaults to "keyup").
12386      */
12387     validationEvent : "keyup",
12388      /**
12389      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12390      */
12391     validateOnBlur : true,
12392     /**
12393      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12394      */
12395     validationDelay : 250,
12396      /**
12397      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12398      */
12399     focusClass : "x-form-focus",  // not needed???
12400     
12401        
12402     /**
12403      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12404      */
12405     invalidClass : "has-warning",
12406     
12407     /**
12408      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12409      */
12410     validClass : "has-success",
12411     
12412     /**
12413      * @cfg {Boolean} hasFeedback (true|false) default true
12414      */
12415     hasFeedback : true,
12416     
12417     /**
12418      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12419      */
12420     invalidFeedbackClass : "glyphicon-warning-sign",
12421     
12422     /**
12423      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12424      */
12425     validFeedbackClass : "glyphicon-ok",
12426     
12427     /**
12428      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12429      */
12430     selectOnFocus : false,
12431     
12432      /**
12433      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12434      */
12435     maskRe : null,
12436        /**
12437      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12438      */
12439     vtype : null,
12440     
12441       /**
12442      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12443      */
12444     disableKeyFilter : false,
12445     
12446        /**
12447      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12448      */
12449     disabled : false,
12450      /**
12451      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12452      */
12453     allowBlank : true,
12454     /**
12455      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12456      */
12457     blankText : "Please complete this mandatory field",
12458     
12459      /**
12460      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12461      */
12462     minLength : 0,
12463     /**
12464      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12465      */
12466     maxLength : Number.MAX_VALUE,
12467     /**
12468      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12469      */
12470     minLengthText : "The minimum length for this field is {0}",
12471     /**
12472      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12473      */
12474     maxLengthText : "The maximum length for this field is {0}",
12475   
12476     
12477     /**
12478      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12479      * If available, this function will be called only after the basic validators all return true, and will be passed the
12480      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12481      */
12482     validator : null,
12483     /**
12484      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12485      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12486      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12487      */
12488     regex : null,
12489     /**
12490      * @cfg {String} regexText -- Depricated - use Invalid Text
12491      */
12492     regexText : "",
12493     
12494     /**
12495      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12496      */
12497     invalidText : "",
12498     
12499     
12500     
12501     autocomplete: false,
12502     
12503     
12504     fieldLabel : '',
12505     inputType : 'text',
12506     
12507     name : false,
12508     placeholder: false,
12509     before : false,
12510     after : false,
12511     size : false,
12512     hasFocus : false,
12513     preventMark: false,
12514     isFormField : true,
12515     value : '',
12516     labelWidth : 2,
12517     labelAlign : false,
12518     readOnly : false,
12519     align : false,
12520     formatedValue : false,
12521     forceFeedback : false,
12522     
12523     indicatorpos : 'left',
12524     
12525     labellg : 0,
12526     labelmd : 0,
12527     labelsm : 0,
12528     labelxs : 0,
12529     
12530     capture : '',
12531     accept : '',
12532     
12533     parentLabelAlign : function()
12534     {
12535         var parent = this;
12536         while (parent.parent()) {
12537             parent = parent.parent();
12538             if (typeof(parent.labelAlign) !='undefined') {
12539                 return parent.labelAlign;
12540             }
12541         }
12542         return 'left';
12543         
12544     },
12545     
12546     getAutoCreate : function()
12547     {
12548         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12549         
12550         var id = Roo.id();
12551         
12552         var cfg = {};
12553         
12554         if(this.inputType != 'hidden'){
12555             cfg.cls = 'form-group' //input-group
12556         }
12557         
12558         var input =  {
12559             tag: 'input',
12560             id : id,
12561             type : this.inputType,
12562             value : this.value,
12563             cls : 'form-control',
12564             placeholder : this.placeholder || '',
12565             autocomplete : this.autocomplete || 'new-password'
12566         };
12567         if (this.inputType == 'file') {
12568             input.style = 'overflow:hidden'; // why not in CSS?
12569         }
12570         
12571         if(this.capture.length){
12572             input.capture = this.capture;
12573         }
12574         
12575         if(this.accept.length){
12576             input.accept = this.accept + "/*";
12577         }
12578         
12579         if(this.align){
12580             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12581         }
12582         
12583         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12584             input.maxLength = this.maxLength;
12585         }
12586         
12587         if (this.disabled) {
12588             input.disabled=true;
12589         }
12590         
12591         if (this.readOnly) {
12592             input.readonly=true;
12593         }
12594         
12595         if (this.name) {
12596             input.name = this.name;
12597         }
12598         
12599         if (this.size) {
12600             input.cls += ' input-' + this.size;
12601         }
12602         
12603         var settings=this;
12604         ['xs','sm','md','lg'].map(function(size){
12605             if (settings[size]) {
12606                 cfg.cls += ' col-' + size + '-' + settings[size];
12607             }
12608         });
12609         
12610         var inputblock = input;
12611         
12612         var feedback = {
12613             tag: 'span',
12614             cls: 'glyphicon form-control-feedback'
12615         };
12616             
12617         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12618             
12619             inputblock = {
12620                 cls : 'has-feedback',
12621                 cn :  [
12622                     input,
12623                     feedback
12624                 ] 
12625             };  
12626         }
12627         
12628         if (this.before || this.after) {
12629             
12630             inputblock = {
12631                 cls : 'input-group',
12632                 cn :  [] 
12633             };
12634             
12635             if (this.before && typeof(this.before) == 'string') {
12636                 
12637                 inputblock.cn.push({
12638                     tag :'span',
12639                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12640                     html : this.before
12641                 });
12642             }
12643             if (this.before && typeof(this.before) == 'object') {
12644                 this.before = Roo.factory(this.before);
12645                 
12646                 inputblock.cn.push({
12647                     tag :'span',
12648                     cls : 'roo-input-before input-group-prepend   input-group-' +
12649                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12650                 });
12651             }
12652             
12653             inputblock.cn.push(input);
12654             
12655             if (this.after && typeof(this.after) == 'string') {
12656                 inputblock.cn.push({
12657                     tag :'span',
12658                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12659                     html : this.after
12660                 });
12661             }
12662             if (this.after && typeof(this.after) == 'object') {
12663                 this.after = Roo.factory(this.after);
12664                 
12665                 inputblock.cn.push({
12666                     tag :'span',
12667                     cls : 'roo-input-after input-group-append  input-group-' +
12668                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12669                 });
12670             }
12671             
12672             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12673                 inputblock.cls += ' has-feedback';
12674                 inputblock.cn.push(feedback);
12675             }
12676         };
12677         var indicator = {
12678             tag : 'i',
12679             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12680             tooltip : 'This field is required'
12681         };
12682         if (this.allowBlank ) {
12683             indicator.style = this.allowBlank ? ' display:none' : '';
12684         }
12685         if (align ==='left' && this.fieldLabel.length) {
12686             
12687             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12688             
12689             cfg.cn = [
12690                 indicator,
12691                 {
12692                     tag: 'label',
12693                     'for' :  id,
12694                     cls : 'control-label col-form-label',
12695                     html : this.fieldLabel
12696
12697                 },
12698                 {
12699                     cls : "", 
12700                     cn: [
12701                         inputblock
12702                     ]
12703                 }
12704             ];
12705             
12706             var labelCfg = cfg.cn[1];
12707             var contentCfg = cfg.cn[2];
12708             
12709             if(this.indicatorpos == 'right'){
12710                 cfg.cn = [
12711                     {
12712                         tag: 'label',
12713                         'for' :  id,
12714                         cls : 'control-label col-form-label',
12715                         cn : [
12716                             {
12717                                 tag : 'span',
12718                                 html : this.fieldLabel
12719                             },
12720                             indicator
12721                         ]
12722                     },
12723                     {
12724                         cls : "",
12725                         cn: [
12726                             inputblock
12727                         ]
12728                     }
12729
12730                 ];
12731                 
12732                 labelCfg = cfg.cn[0];
12733                 contentCfg = cfg.cn[1];
12734             
12735             }
12736             
12737             if(this.labelWidth > 12){
12738                 labelCfg.style = "width: " + this.labelWidth + 'px';
12739             }
12740             
12741             if(this.labelWidth < 13 && this.labelmd == 0){
12742                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12743             }
12744             
12745             if(this.labellg > 0){
12746                 labelCfg.cls += ' col-lg-' + this.labellg;
12747                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12748             }
12749             
12750             if(this.labelmd > 0){
12751                 labelCfg.cls += ' col-md-' + this.labelmd;
12752                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12753             }
12754             
12755             if(this.labelsm > 0){
12756                 labelCfg.cls += ' col-sm-' + this.labelsm;
12757                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12758             }
12759             
12760             if(this.labelxs > 0){
12761                 labelCfg.cls += ' col-xs-' + this.labelxs;
12762                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12763             }
12764             
12765             
12766         } else if ( this.fieldLabel.length) {
12767                 
12768             
12769             
12770             cfg.cn = [
12771                 {
12772                     tag : 'i',
12773                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12774                     tooltip : 'This field is required',
12775                     style : this.allowBlank ? ' display:none' : '' 
12776                 },
12777                 {
12778                     tag: 'label',
12779                    //cls : 'input-group-addon',
12780                     html : this.fieldLabel
12781
12782                 },
12783
12784                inputblock
12785
12786            ];
12787            
12788            if(this.indicatorpos == 'right'){
12789        
12790                 cfg.cn = [
12791                     {
12792                         tag: 'label',
12793                        //cls : 'input-group-addon',
12794                         html : this.fieldLabel
12795
12796                     },
12797                     {
12798                         tag : 'i',
12799                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12800                         tooltip : 'This field is required',
12801                         style : this.allowBlank ? ' display:none' : '' 
12802                     },
12803
12804                    inputblock
12805
12806                ];
12807
12808             }
12809
12810         } else {
12811             
12812             cfg.cn = [
12813
12814                     inputblock
12815
12816             ];
12817                 
12818                 
12819         };
12820         
12821         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12822            cfg.cls += ' navbar-form';
12823         }
12824         
12825         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12826             // on BS4 we do this only if not form 
12827             cfg.cls += ' navbar-form';
12828             cfg.tag = 'li';
12829         }
12830         
12831         return cfg;
12832         
12833     },
12834     /**
12835      * return the real input element.
12836      */
12837     inputEl: function ()
12838     {
12839         return this.el.select('input.form-control',true).first();
12840     },
12841     
12842     tooltipEl : function()
12843     {
12844         return this.inputEl();
12845     },
12846     
12847     indicatorEl : function()
12848     {
12849         if (Roo.bootstrap.version == 4) {
12850             return false; // not enabled in v4 yet.
12851         }
12852         
12853         var indicator = this.el.select('i.roo-required-indicator',true).first();
12854         
12855         if(!indicator){
12856             return false;
12857         }
12858         
12859         return indicator;
12860         
12861     },
12862     
12863     setDisabled : function(v)
12864     {
12865         var i  = this.inputEl().dom;
12866         if (!v) {
12867             i.removeAttribute('disabled');
12868             return;
12869             
12870         }
12871         i.setAttribute('disabled','true');
12872     },
12873     initEvents : function()
12874     {
12875           
12876         this.inputEl().on("keydown" , this.fireKey,  this);
12877         this.inputEl().on("focus", this.onFocus,  this);
12878         this.inputEl().on("blur", this.onBlur,  this);
12879         
12880         this.inputEl().relayEvent('keyup', this);
12881         this.inputEl().relayEvent('paste', this);
12882         
12883         this.indicator = this.indicatorEl();
12884         
12885         if(this.indicator){
12886             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12887         }
12888  
12889         // reference to original value for reset
12890         this.originalValue = this.getValue();
12891         //Roo.form.TextField.superclass.initEvents.call(this);
12892         if(this.validationEvent == 'keyup'){
12893             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12894             this.inputEl().on('keyup', this.filterValidation, this);
12895         }
12896         else if(this.validationEvent !== false){
12897             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12898         }
12899         
12900         if(this.selectOnFocus){
12901             this.on("focus", this.preFocus, this);
12902             
12903         }
12904         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12905             this.inputEl().on("keypress", this.filterKeys, this);
12906         } else {
12907             this.inputEl().relayEvent('keypress', this);
12908         }
12909        /* if(this.grow){
12910             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12911             this.el.on("click", this.autoSize,  this);
12912         }
12913         */
12914         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12915             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12916         }
12917         
12918         if (typeof(this.before) == 'object') {
12919             this.before.render(this.el.select('.roo-input-before',true).first());
12920         }
12921         if (typeof(this.after) == 'object') {
12922             this.after.render(this.el.select('.roo-input-after',true).first());
12923         }
12924         
12925         this.inputEl().on('change', this.onChange, this);
12926         
12927     },
12928     filterValidation : function(e){
12929         if(!e.isNavKeyPress()){
12930             this.validationTask.delay(this.validationDelay);
12931         }
12932     },
12933      /**
12934      * Validates the field value
12935      * @return {Boolean} True if the value is valid, else false
12936      */
12937     validate : function(){
12938         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12939         if(this.disabled || this.validateValue(this.getRawValue())){
12940             this.markValid();
12941             return true;
12942         }
12943         
12944         this.markInvalid();
12945         return false;
12946     },
12947     
12948     
12949     /**
12950      * Validates a value according to the field's validation rules and marks the field as invalid
12951      * if the validation fails
12952      * @param {Mixed} value The value to validate
12953      * @return {Boolean} True if the value is valid, else false
12954      */
12955     validateValue : function(value)
12956     {
12957         if(this.getVisibilityEl().hasClass('hidden')){
12958             return true;
12959         }
12960         
12961         if(value.length < 1)  { // if it's blank
12962             if(this.allowBlank){
12963                 return true;
12964             }
12965             return false;
12966         }
12967         
12968         if(value.length < this.minLength){
12969             return false;
12970         }
12971         if(value.length > this.maxLength){
12972             return false;
12973         }
12974         if(this.vtype){
12975             var vt = Roo.form.VTypes;
12976             if(!vt[this.vtype](value, this)){
12977                 return false;
12978             }
12979         }
12980         if(typeof this.validator == "function"){
12981             var msg = this.validator(value);
12982             if (typeof(msg) == 'string') {
12983                 this.invalidText = msg;
12984             }
12985             if(msg !== true){
12986                 return false;
12987             }
12988         }
12989         
12990         if(this.regex && !this.regex.test(value)){
12991             return false;
12992         }
12993         
12994         return true;
12995     },
12996     
12997      // private
12998     fireKey : function(e){
12999         //Roo.log('field ' + e.getKey());
13000         if(e.isNavKeyPress()){
13001             this.fireEvent("specialkey", this, e);
13002         }
13003     },
13004     focus : function (selectText){
13005         if(this.rendered){
13006             this.inputEl().focus();
13007             if(selectText === true){
13008                 this.inputEl().dom.select();
13009             }
13010         }
13011         return this;
13012     } ,
13013     
13014     onFocus : function(){
13015         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13016            // this.el.addClass(this.focusClass);
13017         }
13018         if(!this.hasFocus){
13019             this.hasFocus = true;
13020             this.startValue = this.getValue();
13021             this.fireEvent("focus", this);
13022         }
13023     },
13024     
13025     beforeBlur : Roo.emptyFn,
13026
13027     
13028     // private
13029     onBlur : function(){
13030         this.beforeBlur();
13031         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13032             //this.el.removeClass(this.focusClass);
13033         }
13034         this.hasFocus = false;
13035         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13036             this.validate();
13037         }
13038         var v = this.getValue();
13039         if(String(v) !== String(this.startValue)){
13040             this.fireEvent('change', this, v, this.startValue);
13041         }
13042         this.fireEvent("blur", this);
13043     },
13044     
13045     onChange : function(e)
13046     {
13047         var v = this.getValue();
13048         if(String(v) !== String(this.startValue)){
13049             this.fireEvent('change', this, v, this.startValue);
13050         }
13051         
13052     },
13053     
13054     /**
13055      * Resets the current field value to the originally loaded value and clears any validation messages
13056      */
13057     reset : function(){
13058         this.setValue(this.originalValue);
13059         this.validate();
13060     },
13061      /**
13062      * Returns the name of the field
13063      * @return {Mixed} name The name field
13064      */
13065     getName: function(){
13066         return this.name;
13067     },
13068      /**
13069      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13070      * @return {Mixed} value The field value
13071      */
13072     getValue : function(){
13073         
13074         var v = this.inputEl().getValue();
13075         
13076         return v;
13077     },
13078     /**
13079      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13080      * @return {Mixed} value The field value
13081      */
13082     getRawValue : function(){
13083         var v = this.inputEl().getValue();
13084         
13085         return v;
13086     },
13087     
13088     /**
13089      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13090      * @param {Mixed} value The value to set
13091      */
13092     setRawValue : function(v){
13093         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13094     },
13095     
13096     selectText : function(start, end){
13097         var v = this.getRawValue();
13098         if(v.length > 0){
13099             start = start === undefined ? 0 : start;
13100             end = end === undefined ? v.length : end;
13101             var d = this.inputEl().dom;
13102             if(d.setSelectionRange){
13103                 d.setSelectionRange(start, end);
13104             }else if(d.createTextRange){
13105                 var range = d.createTextRange();
13106                 range.moveStart("character", start);
13107                 range.moveEnd("character", v.length-end);
13108                 range.select();
13109             }
13110         }
13111     },
13112     
13113     /**
13114      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13115      * @param {Mixed} value The value to set
13116      */
13117     setValue : function(v){
13118         this.value = v;
13119         if(this.rendered){
13120             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13121             this.validate();
13122         }
13123     },
13124     
13125     /*
13126     processValue : function(value){
13127         if(this.stripCharsRe){
13128             var newValue = value.replace(this.stripCharsRe, '');
13129             if(newValue !== value){
13130                 this.setRawValue(newValue);
13131                 return newValue;
13132             }
13133         }
13134         return value;
13135     },
13136   */
13137     preFocus : function(){
13138         
13139         if(this.selectOnFocus){
13140             this.inputEl().dom.select();
13141         }
13142     },
13143     filterKeys : function(e){
13144         var k = e.getKey();
13145         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13146             return;
13147         }
13148         var c = e.getCharCode(), cc = String.fromCharCode(c);
13149         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13150             return;
13151         }
13152         if(!this.maskRe.test(cc)){
13153             e.stopEvent();
13154         }
13155     },
13156      /**
13157      * Clear any invalid styles/messages for this field
13158      */
13159     clearInvalid : function(){
13160         
13161         if(!this.el || this.preventMark){ // not rendered
13162             return;
13163         }
13164         
13165         
13166         this.el.removeClass([this.invalidClass, 'is-invalid']);
13167         
13168         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13169             
13170             var feedback = this.el.select('.form-control-feedback', true).first();
13171             
13172             if(feedback){
13173                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13174             }
13175             
13176         }
13177         
13178         if(this.indicator){
13179             this.indicator.removeClass('visible');
13180             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13181         }
13182         
13183         this.fireEvent('valid', this);
13184     },
13185     
13186      /**
13187      * Mark this field as valid
13188      */
13189     markValid : function()
13190     {
13191         if(!this.el  || this.preventMark){ // not rendered...
13192             return;
13193         }
13194         
13195         this.el.removeClass([this.invalidClass, this.validClass]);
13196         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13197
13198         var feedback = this.el.select('.form-control-feedback', true).first();
13199             
13200         if(feedback){
13201             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13202         }
13203         
13204         if(this.indicator){
13205             this.indicator.removeClass('visible');
13206             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13207         }
13208         
13209         if(this.disabled){
13210             return;
13211         }
13212         
13213            
13214         if(this.allowBlank && !this.getRawValue().length){
13215             return;
13216         }
13217         if (Roo.bootstrap.version == 3) {
13218             this.el.addClass(this.validClass);
13219         } else {
13220             this.inputEl().addClass('is-valid');
13221         }
13222
13223         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13224             
13225             var feedback = this.el.select('.form-control-feedback', true).first();
13226             
13227             if(feedback){
13228                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13229                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13230             }
13231             
13232         }
13233         
13234         this.fireEvent('valid', this);
13235     },
13236     
13237      /**
13238      * Mark this field as invalid
13239      * @param {String} msg The validation message
13240      */
13241     markInvalid : function(msg)
13242     {
13243         if(!this.el  || this.preventMark){ // not rendered
13244             return;
13245         }
13246         
13247         this.el.removeClass([this.invalidClass, this.validClass]);
13248         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13249         
13250         var feedback = this.el.select('.form-control-feedback', true).first();
13251             
13252         if(feedback){
13253             this.el.select('.form-control-feedback', true).first().removeClass(
13254                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13255         }
13256
13257         if(this.disabled){
13258             return;
13259         }
13260         
13261         if(this.allowBlank && !this.getRawValue().length){
13262             return;
13263         }
13264         
13265         if(this.indicator){
13266             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13267             this.indicator.addClass('visible');
13268         }
13269         if (Roo.bootstrap.version == 3) {
13270             this.el.addClass(this.invalidClass);
13271         } else {
13272             this.inputEl().addClass('is-invalid');
13273         }
13274         
13275         
13276         
13277         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13278             
13279             var feedback = this.el.select('.form-control-feedback', true).first();
13280             
13281             if(feedback){
13282                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13283                 
13284                 if(this.getValue().length || this.forceFeedback){
13285                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13286                 }
13287                 
13288             }
13289             
13290         }
13291         
13292         this.fireEvent('invalid', this, msg);
13293     },
13294     // private
13295     SafariOnKeyDown : function(event)
13296     {
13297         // this is a workaround for a password hang bug on chrome/ webkit.
13298         if (this.inputEl().dom.type != 'password') {
13299             return;
13300         }
13301         
13302         var isSelectAll = false;
13303         
13304         if(this.inputEl().dom.selectionEnd > 0){
13305             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13306         }
13307         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13308             event.preventDefault();
13309             this.setValue('');
13310             return;
13311         }
13312         
13313         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13314             
13315             event.preventDefault();
13316             // this is very hacky as keydown always get's upper case.
13317             //
13318             var cc = String.fromCharCode(event.getCharCode());
13319             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13320             
13321         }
13322     },
13323     adjustWidth : function(tag, w){
13324         tag = tag.toLowerCase();
13325         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13326             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13327                 if(tag == 'input'){
13328                     return w + 2;
13329                 }
13330                 if(tag == 'textarea'){
13331                     return w-2;
13332                 }
13333             }else if(Roo.isOpera){
13334                 if(tag == 'input'){
13335                     return w + 2;
13336                 }
13337                 if(tag == 'textarea'){
13338                     return w-2;
13339                 }
13340             }
13341         }
13342         return w;
13343     },
13344     
13345     setFieldLabel : function(v)
13346     {
13347         if(!this.rendered){
13348             return;
13349         }
13350         
13351         if(this.indicatorEl()){
13352             var ar = this.el.select('label > span',true);
13353             
13354             if (ar.elements.length) {
13355                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13356                 this.fieldLabel = v;
13357                 return;
13358             }
13359             
13360             var br = this.el.select('label',true);
13361             
13362             if(br.elements.length) {
13363                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13364                 this.fieldLabel = v;
13365                 return;
13366             }
13367             
13368             Roo.log('Cannot Found any of label > span || label in input');
13369             return;
13370         }
13371         
13372         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13373         this.fieldLabel = v;
13374         
13375         
13376     }
13377 });
13378
13379  
13380 /*
13381  * - LGPL
13382  *
13383  * Input
13384  * 
13385  */
13386
13387 /**
13388  * @class Roo.bootstrap.form.TextArea
13389  * @extends Roo.bootstrap.form.Input
13390  * Bootstrap TextArea class
13391  * @cfg {Number} cols Specifies the visible width of a text area
13392  * @cfg {Number} rows Specifies the visible number of lines in a text area
13393  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13394  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13395  * @cfg {string} html text
13396  * 
13397  * @constructor
13398  * Create a new TextArea
13399  * @param {Object} config The config object
13400  */
13401
13402 Roo.bootstrap.form.TextArea = function(config){
13403     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13404    
13405 };
13406
13407 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13408      
13409     cols : false,
13410     rows : 5,
13411     readOnly : false,
13412     warp : 'soft',
13413     resize : false,
13414     value: false,
13415     html: false,
13416     
13417     getAutoCreate : function(){
13418         
13419         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13420         
13421         var id = Roo.id();
13422         
13423         var cfg = {};
13424         
13425         if(this.inputType != 'hidden'){
13426             cfg.cls = 'form-group' //input-group
13427         }
13428         
13429         var input =  {
13430             tag: 'textarea',
13431             id : id,
13432             warp : this.warp,
13433             rows : this.rows,
13434             value : this.value || '',
13435             html: this.html || '',
13436             cls : 'form-control',
13437             placeholder : this.placeholder || '' 
13438             
13439         };
13440         
13441         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13442             input.maxLength = this.maxLength;
13443         }
13444         
13445         if(this.resize){
13446             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13447         }
13448         
13449         if(this.cols){
13450             input.cols = this.cols;
13451         }
13452         
13453         if (this.readOnly) {
13454             input.readonly = true;
13455         }
13456         
13457         if (this.name) {
13458             input.name = this.name;
13459         }
13460         
13461         if (this.size) {
13462             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13463         }
13464         
13465         var settings=this;
13466         ['xs','sm','md','lg'].map(function(size){
13467             if (settings[size]) {
13468                 cfg.cls += ' col-' + size + '-' + settings[size];
13469             }
13470         });
13471         
13472         var inputblock = input;
13473         
13474         if(this.hasFeedback && !this.allowBlank){
13475             
13476             var feedback = {
13477                 tag: 'span',
13478                 cls: 'glyphicon form-control-feedback'
13479             };
13480
13481             inputblock = {
13482                 cls : 'has-feedback',
13483                 cn :  [
13484                     input,
13485                     feedback
13486                 ] 
13487             };  
13488         }
13489         
13490         
13491         if (this.before || this.after) {
13492             
13493             inputblock = {
13494                 cls : 'input-group',
13495                 cn :  [] 
13496             };
13497             if (this.before) {
13498                 inputblock.cn.push({
13499                     tag :'span',
13500                     cls : 'input-group-addon',
13501                     html : this.before
13502                 });
13503             }
13504             
13505             inputblock.cn.push(input);
13506             
13507             if(this.hasFeedback && !this.allowBlank){
13508                 inputblock.cls += ' has-feedback';
13509                 inputblock.cn.push(feedback);
13510             }
13511             
13512             if (this.after) {
13513                 inputblock.cn.push({
13514                     tag :'span',
13515                     cls : 'input-group-addon',
13516                     html : this.after
13517                 });
13518             }
13519             
13520         }
13521         
13522         if (align ==='left' && this.fieldLabel.length) {
13523             cfg.cn = [
13524                 {
13525                     tag: 'label',
13526                     'for' :  id,
13527                     cls : 'control-label',
13528                     html : this.fieldLabel
13529                 },
13530                 {
13531                     cls : "",
13532                     cn: [
13533                         inputblock
13534                     ]
13535                 }
13536
13537             ];
13538             
13539             if(this.labelWidth > 12){
13540                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13541             }
13542
13543             if(this.labelWidth < 13 && this.labelmd == 0){
13544                 this.labelmd = this.labelWidth;
13545             }
13546
13547             if(this.labellg > 0){
13548                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13549                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13550             }
13551
13552             if(this.labelmd > 0){
13553                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13554                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13555             }
13556
13557             if(this.labelsm > 0){
13558                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13559                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13560             }
13561
13562             if(this.labelxs > 0){
13563                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13564                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13565             }
13566             
13567         } else if ( this.fieldLabel.length) {
13568             cfg.cn = [
13569
13570                {
13571                    tag: 'label',
13572                    //cls : 'input-group-addon',
13573                    html : this.fieldLabel
13574
13575                },
13576
13577                inputblock
13578
13579            ];
13580
13581         } else {
13582
13583             cfg.cn = [
13584
13585                 inputblock
13586
13587             ];
13588                 
13589         }
13590         
13591         if (this.disabled) {
13592             input.disabled=true;
13593         }
13594         
13595         return cfg;
13596         
13597     },
13598     /**
13599      * return the real textarea element.
13600      */
13601     inputEl: function ()
13602     {
13603         return this.el.select('textarea.form-control',true).first();
13604     },
13605     
13606     /**
13607      * Clear any invalid styles/messages for this field
13608      */
13609     clearInvalid : function()
13610     {
13611         
13612         if(!this.el || this.preventMark){ // not rendered
13613             return;
13614         }
13615         
13616         var label = this.el.select('label', true).first();
13617         var icon = this.el.select('i.fa-star', true).first();
13618         
13619         if(label && icon){
13620             icon.remove();
13621         }
13622         this.el.removeClass( this.validClass);
13623         this.inputEl().removeClass('is-invalid');
13624          
13625         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13626             
13627             var feedback = this.el.select('.form-control-feedback', true).first();
13628             
13629             if(feedback){
13630                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13631             }
13632             
13633         }
13634         
13635         this.fireEvent('valid', this);
13636     },
13637     
13638      /**
13639      * Mark this field as valid
13640      */
13641     markValid : function()
13642     {
13643         if(!this.el  || this.preventMark){ // not rendered
13644             return;
13645         }
13646         
13647         this.el.removeClass([this.invalidClass, this.validClass]);
13648         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13649         
13650         var feedback = this.el.select('.form-control-feedback', true).first();
13651             
13652         if(feedback){
13653             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13654         }
13655
13656         if(this.disabled || this.allowBlank){
13657             return;
13658         }
13659         
13660         var label = this.el.select('label', true).first();
13661         var icon = this.el.select('i.fa-star', true).first();
13662         
13663         if(label && icon){
13664             icon.remove();
13665         }
13666         if (Roo.bootstrap.version == 3) {
13667             this.el.addClass(this.validClass);
13668         } else {
13669             this.inputEl().addClass('is-valid');
13670         }
13671         
13672         
13673         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13674             
13675             var feedback = this.el.select('.form-control-feedback', true).first();
13676             
13677             if(feedback){
13678                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13679                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13680             }
13681             
13682         }
13683         
13684         this.fireEvent('valid', this);
13685     },
13686     
13687      /**
13688      * Mark this field as invalid
13689      * @param {String} msg The validation message
13690      */
13691     markInvalid : function(msg)
13692     {
13693         if(!this.el  || this.preventMark){ // not rendered
13694             return;
13695         }
13696         
13697         this.el.removeClass([this.invalidClass, this.validClass]);
13698         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13699         
13700         var feedback = this.el.select('.form-control-feedback', true).first();
13701             
13702         if(feedback){
13703             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13704         }
13705
13706         if(this.disabled || this.allowBlank){
13707             return;
13708         }
13709         
13710         var label = this.el.select('label', true).first();
13711         var icon = this.el.select('i.fa-star', true).first();
13712         
13713         if(!this.getValue().length && label && !icon){
13714             this.el.createChild({
13715                 tag : 'i',
13716                 cls : 'text-danger fa fa-lg fa-star',
13717                 tooltip : 'This field is required',
13718                 style : 'margin-right:5px;'
13719             }, label, true);
13720         }
13721         
13722         if (Roo.bootstrap.version == 3) {
13723             this.el.addClass(this.invalidClass);
13724         } else {
13725             this.inputEl().addClass('is-invalid');
13726         }
13727         
13728         // fixme ... this may be depricated need to test..
13729         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13730             
13731             var feedback = this.el.select('.form-control-feedback', true).first();
13732             
13733             if(feedback){
13734                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13735                 
13736                 if(this.getValue().length || this.forceFeedback){
13737                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13738                 }
13739                 
13740             }
13741             
13742         }
13743         
13744         this.fireEvent('invalid', this, msg);
13745     }
13746 });
13747
13748  
13749 /*
13750  * - LGPL
13751  *
13752  * trigger field - base class for combo..
13753  * 
13754  */
13755  
13756 /**
13757  * @class Roo.bootstrap.form.TriggerField
13758  * @extends Roo.bootstrap.form.Input
13759  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13760  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13761  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13762  * for which you can provide a custom implementation.  For example:
13763  * <pre><code>
13764 var trigger = new Roo.bootstrap.form.TriggerField();
13765 trigger.onTriggerClick = myTriggerFn;
13766 trigger.applyTo('my-field');
13767 </code></pre>
13768  *
13769  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13770  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13771  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13772  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13773  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13774
13775  * @constructor
13776  * Create a new TriggerField.
13777  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13778  * to the base TextField)
13779  */
13780 Roo.bootstrap.form.TriggerField = function(config){
13781     this.mimicing = false;
13782     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13783 };
13784
13785 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13786     /**
13787      * @cfg {String} triggerClass A CSS class to apply to the trigger
13788      */
13789      /**
13790      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13791      */
13792     hideTrigger:false,
13793
13794     /**
13795      * @cfg {Boolean} removable (true|false) special filter default false
13796      */
13797     removable : false,
13798     
13799     /** @cfg {Boolean} grow @hide */
13800     /** @cfg {Number} growMin @hide */
13801     /** @cfg {Number} growMax @hide */
13802
13803     /**
13804      * @hide 
13805      * @method
13806      */
13807     autoSize: Roo.emptyFn,
13808     // private
13809     monitorTab : true,
13810     // private
13811     deferHeight : true,
13812
13813     
13814     actionMode : 'wrap',
13815     
13816     caret : false,
13817     
13818     
13819     getAutoCreate : function(){
13820        
13821         var align = this.labelAlign || this.parentLabelAlign();
13822         
13823         var id = Roo.id();
13824         
13825         var cfg = {
13826             cls: 'form-group' //input-group
13827         };
13828         
13829         
13830         var input =  {
13831             tag: 'input',
13832             id : id,
13833             type : this.inputType,
13834             cls : 'form-control',
13835             autocomplete: 'new-password',
13836             placeholder : this.placeholder || '' 
13837             
13838         };
13839         if (this.name) {
13840             input.name = this.name;
13841         }
13842         if (this.size) {
13843             input.cls += ' input-' + this.size;
13844         }
13845         
13846         if (this.disabled) {
13847             input.disabled=true;
13848         }
13849         
13850         var inputblock = input;
13851         
13852         if(this.hasFeedback && !this.allowBlank){
13853             
13854             var feedback = {
13855                 tag: 'span',
13856                 cls: 'glyphicon form-control-feedback'
13857             };
13858             
13859             if(this.removable && !this.editable  ){
13860                 inputblock = {
13861                     cls : 'has-feedback',
13862                     cn :  [
13863                         inputblock,
13864                         {
13865                             tag: 'button',
13866                             html : 'x',
13867                             cls : 'roo-combo-removable-btn close'
13868                         },
13869                         feedback
13870                     ] 
13871                 };
13872             } else {
13873                 inputblock = {
13874                     cls : 'has-feedback',
13875                     cn :  [
13876                         inputblock,
13877                         feedback
13878                     ] 
13879                 };
13880             }
13881
13882         } else {
13883             if(this.removable && !this.editable ){
13884                 inputblock = {
13885                     cls : 'roo-removable',
13886                     cn :  [
13887                         inputblock,
13888                         {
13889                             tag: 'button',
13890                             html : 'x',
13891                             cls : 'roo-combo-removable-btn close'
13892                         }
13893                     ] 
13894                 };
13895             }
13896         }
13897         
13898         if (this.before || this.after) {
13899             
13900             inputblock = {
13901                 cls : 'input-group',
13902                 cn :  [] 
13903             };
13904             if (this.before) {
13905                 inputblock.cn.push({
13906                     tag :'span',
13907                     cls : 'input-group-addon input-group-prepend input-group-text',
13908                     html : this.before
13909                 });
13910             }
13911             
13912             inputblock.cn.push(input);
13913             
13914             if(this.hasFeedback && !this.allowBlank){
13915                 inputblock.cls += ' has-feedback';
13916                 inputblock.cn.push(feedback);
13917             }
13918             
13919             if (this.after) {
13920                 inputblock.cn.push({
13921                     tag :'span',
13922                     cls : 'input-group-addon input-group-append input-group-text',
13923                     html : this.after
13924                 });
13925             }
13926             
13927         };
13928         
13929       
13930         
13931         var ibwrap = inputblock;
13932         
13933         if(this.multiple){
13934             ibwrap = {
13935                 tag: 'ul',
13936                 cls: 'roo-select2-choices',
13937                 cn:[
13938                     {
13939                         tag: 'li',
13940                         cls: 'roo-select2-search-field',
13941                         cn: [
13942
13943                             inputblock
13944                         ]
13945                     }
13946                 ]
13947             };
13948                 
13949         }
13950         
13951         var combobox = {
13952             cls: 'roo-select2-container input-group',
13953             cn: [
13954                  {
13955                     tag: 'input',
13956                     type : 'hidden',
13957                     cls: 'form-hidden-field'
13958                 },
13959                 ibwrap
13960             ]
13961         };
13962         
13963         if(!this.multiple && this.showToggleBtn){
13964             
13965             var caret = {
13966                         tag: 'span',
13967                         cls: 'caret'
13968              };
13969             if (this.caret != false) {
13970                 caret = {
13971                      tag: 'i',
13972                      cls: 'fa fa-' + this.caret
13973                 };
13974                 
13975             }
13976             
13977             combobox.cn.push({
13978                 tag :'span',
13979                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13980                 cn : [
13981                     Roo.bootstrap.version == 3 ? caret : '',
13982                     {
13983                         tag: 'span',
13984                         cls: 'combobox-clear',
13985                         cn  : [
13986                             {
13987                                 tag : 'i',
13988                                 cls: 'icon-remove'
13989                             }
13990                         ]
13991                     }
13992                 ]
13993
13994             })
13995         }
13996         
13997         if(this.multiple){
13998             combobox.cls += ' roo-select2-container-multi';
13999         }
14000          var indicator = {
14001             tag : 'i',
14002             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
14003             tooltip : 'This field is required'
14004         };
14005         if (Roo.bootstrap.version == 4) {
14006             indicator = {
14007                 tag : 'i',
14008                 style : 'display:none'
14009             };
14010         }
14011         
14012         
14013         if (align ==='left' && this.fieldLabel.length) {
14014             
14015             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
14016
14017             cfg.cn = [
14018                 indicator,
14019                 {
14020                     tag: 'label',
14021                     'for' :  id,
14022                     cls : 'control-label',
14023                     html : this.fieldLabel
14024
14025                 },
14026                 {
14027                     cls : "", 
14028                     cn: [
14029                         combobox
14030                     ]
14031                 }
14032
14033             ];
14034             
14035             var labelCfg = cfg.cn[1];
14036             var contentCfg = cfg.cn[2];
14037             
14038             if(this.indicatorpos == 'right'){
14039                 cfg.cn = [
14040                     {
14041                         tag: 'label',
14042                         'for' :  id,
14043                         cls : 'control-label',
14044                         cn : [
14045                             {
14046                                 tag : 'span',
14047                                 html : this.fieldLabel
14048                             },
14049                             indicator
14050                         ]
14051                     },
14052                     {
14053                         cls : "", 
14054                         cn: [
14055                             combobox
14056                         ]
14057                     }
14058
14059                 ];
14060                 
14061                 labelCfg = cfg.cn[0];
14062                 contentCfg = cfg.cn[1];
14063             }
14064             
14065             if(this.labelWidth > 12){
14066                 labelCfg.style = "width: " + this.labelWidth + 'px';
14067             }
14068             
14069             if(this.labelWidth < 13 && this.labelmd == 0){
14070                 this.labelmd = this.labelWidth;
14071             }
14072             
14073             if(this.labellg > 0){
14074                 labelCfg.cls += ' col-lg-' + this.labellg;
14075                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14076             }
14077             
14078             if(this.labelmd > 0){
14079                 labelCfg.cls += ' col-md-' + this.labelmd;
14080                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14081             }
14082             
14083             if(this.labelsm > 0){
14084                 labelCfg.cls += ' col-sm-' + this.labelsm;
14085                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14086             }
14087             
14088             if(this.labelxs > 0){
14089                 labelCfg.cls += ' col-xs-' + this.labelxs;
14090                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14091             }
14092             
14093         } else if ( this.fieldLabel.length) {
14094 //                Roo.log(" label");
14095             cfg.cn = [
14096                 indicator,
14097                {
14098                    tag: 'label',
14099                    //cls : 'input-group-addon',
14100                    html : this.fieldLabel
14101
14102                },
14103
14104                combobox
14105
14106             ];
14107             
14108             if(this.indicatorpos == 'right'){
14109                 
14110                 cfg.cn = [
14111                     {
14112                        tag: 'label',
14113                        cn : [
14114                            {
14115                                tag : 'span',
14116                                html : this.fieldLabel
14117                            },
14118                            indicator
14119                        ]
14120
14121                     },
14122                     combobox
14123
14124                 ];
14125
14126             }
14127
14128         } else {
14129             
14130 //                Roo.log(" no label && no align");
14131                 cfg = combobox
14132                      
14133                 
14134         }
14135         
14136         var settings=this;
14137         ['xs','sm','md','lg'].map(function(size){
14138             if (settings[size]) {
14139                 cfg.cls += ' col-' + size + '-' + settings[size];
14140             }
14141         });
14142         
14143         return cfg;
14144         
14145     },
14146     
14147     
14148     
14149     // private
14150     onResize : function(w, h){
14151 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14152 //        if(typeof w == 'number'){
14153 //            var x = w - this.trigger.getWidth();
14154 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14155 //            this.trigger.setStyle('left', x+'px');
14156 //        }
14157     },
14158
14159     // private
14160     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14161
14162     // private
14163     getResizeEl : function(){
14164         return this.inputEl();
14165     },
14166
14167     // private
14168     getPositionEl : function(){
14169         return this.inputEl();
14170     },
14171
14172     // private
14173     alignErrorIcon : function(){
14174         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14175     },
14176
14177     // private
14178     initEvents : function(){
14179         
14180         this.createList();
14181         
14182         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14183         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14184         if(!this.multiple && this.showToggleBtn){
14185             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14186             if(this.hideTrigger){
14187                 this.trigger.setDisplayed(false);
14188             }
14189             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14190         }
14191         
14192         if(this.multiple){
14193             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14194         }
14195         
14196         if(this.removable && !this.editable && !this.tickable){
14197             var close = this.closeTriggerEl();
14198             
14199             if(close){
14200                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14201                 close.on('click', this.removeBtnClick, this, close);
14202             }
14203         }
14204         
14205         //this.trigger.addClassOnOver('x-form-trigger-over');
14206         //this.trigger.addClassOnClick('x-form-trigger-click');
14207         
14208         //if(!this.width){
14209         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14210         //}
14211     },
14212     
14213     closeTriggerEl : function()
14214     {
14215         var close = this.el.select('.roo-combo-removable-btn', true).first();
14216         return close ? close : false;
14217     },
14218     
14219     removeBtnClick : function(e, h, el)
14220     {
14221         e.preventDefault();
14222         
14223         if(this.fireEvent("remove", this) !== false){
14224             this.reset();
14225             this.fireEvent("afterremove", this)
14226         }
14227     },
14228     
14229     createList : function()
14230     {
14231         this.list = Roo.get(document.body).createChild({
14232             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14233             cls: 'typeahead typeahead-long dropdown-menu shadow',
14234             style: 'display:none'
14235         });
14236         
14237         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14238         
14239     },
14240
14241     // private
14242     initTrigger : function(){
14243        
14244     },
14245
14246     // private
14247     onDestroy : function(){
14248         if(this.trigger){
14249             this.trigger.removeAllListeners();
14250           //  this.trigger.remove();
14251         }
14252         //if(this.wrap){
14253         //    this.wrap.remove();
14254         //}
14255         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14256     },
14257
14258     // private
14259     onFocus : function(){
14260         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14261         /*
14262         if(!this.mimicing){
14263             this.wrap.addClass('x-trigger-wrap-focus');
14264             this.mimicing = true;
14265             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14266             if(this.monitorTab){
14267                 this.el.on("keydown", this.checkTab, this);
14268             }
14269         }
14270         */
14271     },
14272
14273     // private
14274     checkTab : function(e){
14275         if(e.getKey() == e.TAB){
14276             this.triggerBlur();
14277         }
14278     },
14279
14280     // private
14281     onBlur : function(){
14282         // do nothing
14283     },
14284
14285     // private
14286     mimicBlur : function(e, t){
14287         /*
14288         if(!this.wrap.contains(t) && this.validateBlur()){
14289             this.triggerBlur();
14290         }
14291         */
14292     },
14293
14294     // private
14295     triggerBlur : function(){
14296         this.mimicing = false;
14297         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14298         if(this.monitorTab){
14299             this.el.un("keydown", this.checkTab, this);
14300         }
14301         //this.wrap.removeClass('x-trigger-wrap-focus');
14302         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14303     },
14304
14305     // private
14306     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14307     validateBlur : function(e, t){
14308         return true;
14309     },
14310
14311     // private
14312     onDisable : function(){
14313         this.inputEl().dom.disabled = true;
14314         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14315         //if(this.wrap){
14316         //    this.wrap.addClass('x-item-disabled');
14317         //}
14318     },
14319
14320     // private
14321     onEnable : function(){
14322         this.inputEl().dom.disabled = false;
14323         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14324         //if(this.wrap){
14325         //    this.el.removeClass('x-item-disabled');
14326         //}
14327     },
14328
14329     // private
14330     onShow : function(){
14331         var ae = this.getActionEl();
14332         
14333         if(ae){
14334             ae.dom.style.display = '';
14335             ae.dom.style.visibility = 'visible';
14336         }
14337     },
14338
14339     // private
14340     
14341     onHide : function(){
14342         var ae = this.getActionEl();
14343         ae.dom.style.display = 'none';
14344     },
14345
14346     /**
14347      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14348      * by an implementing function.
14349      * @method
14350      * @param {EventObject} e
14351      */
14352     onTriggerClick : Roo.emptyFn
14353 });
14354  
14355 /*
14356 * Licence: LGPL
14357 */
14358
14359 /**
14360  * @class Roo.bootstrap.form.CardUploader
14361  * @extends Roo.bootstrap.Button
14362  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14363  * @cfg {Number} errorTimeout default 3000
14364  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14365  * @cfg {Array}  html The button text.
14366
14367  *
14368  * @constructor
14369  * Create a new CardUploader
14370  * @param {Object} config The config object
14371  */
14372
14373 Roo.bootstrap.form.CardUploader = function(config){
14374     
14375  
14376     
14377     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14378     
14379     
14380     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14381         return r.data.id
14382      });
14383     
14384      this.addEvents({
14385          // raw events
14386         /**
14387          * @event preview
14388          * When a image is clicked on - and needs to display a slideshow or similar..
14389          * @param {Roo.bootstrap.Card} this
14390          * @param {Object} The image information data 
14391          *
14392          */
14393         'preview' : true,
14394          /**
14395          * @event download
14396          * When a the download link is clicked
14397          * @param {Roo.bootstrap.Card} this
14398          * @param {Object} The image information data  contains 
14399          */
14400         'download' : true
14401         
14402     });
14403 };
14404  
14405 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14406     
14407      
14408     errorTimeout : 3000,
14409      
14410     images : false,
14411    
14412     fileCollection : false,
14413     allowBlank : true,
14414     
14415     getAutoCreate : function()
14416     {
14417         
14418         var cfg =  {
14419             cls :'form-group' ,
14420             cn : [
14421                
14422                 {
14423                     tag: 'label',
14424                    //cls : 'input-group-addon',
14425                     html : this.fieldLabel
14426
14427                 },
14428
14429                 {
14430                     tag: 'input',
14431                     type : 'hidden',
14432                     name : this.name,
14433                     value : this.value,
14434                     cls : 'd-none  form-control'
14435                 },
14436                 
14437                 {
14438                     tag: 'input',
14439                     multiple : 'multiple',
14440                     type : 'file',
14441                     cls : 'd-none  roo-card-upload-selector'
14442                 },
14443                 
14444                 {
14445                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14446                 },
14447                 {
14448                     cls : 'card-columns roo-card-uploader-container'
14449                 }
14450
14451             ]
14452         };
14453            
14454          
14455         return cfg;
14456     },
14457     
14458     getChildContainer : function() /// what children are added to.
14459     {
14460         return this.containerEl;
14461     },
14462    
14463     getButtonContainer : function() /// what children are added to.
14464     {
14465         return this.el.select(".roo-card-uploader-button-container").first();
14466     },
14467    
14468     initEvents : function()
14469     {
14470         
14471         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14472         
14473         var t = this;
14474         this.addxtype({
14475             xns: Roo.bootstrap,
14476
14477             xtype : 'Button',
14478             container_method : 'getButtonContainer' ,            
14479             html :  this.html, // fix changable?
14480             cls : 'w-100 ',
14481             listeners : {
14482                 'click' : function(btn, e) {
14483                     t.onClick(e);
14484                 }
14485             }
14486         });
14487         
14488         
14489         
14490         
14491         this.urlAPI = (window.createObjectURL && window) || 
14492                                 (window.URL && URL.revokeObjectURL && URL) || 
14493                                 (window.webkitURL && webkitURL);
14494                         
14495          
14496          
14497          
14498         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14499         
14500         this.selectorEl.on('change', this.onFileSelected, this);
14501         if (this.images) {
14502             var t = this;
14503             this.images.forEach(function(img) {
14504                 t.addCard(img)
14505             });
14506             this.images = false;
14507         }
14508         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14509          
14510        
14511     },
14512     
14513    
14514     onClick : function(e)
14515     {
14516         e.preventDefault();
14517          
14518         this.selectorEl.dom.click();
14519          
14520     },
14521     
14522     onFileSelected : function(e)
14523     {
14524         e.preventDefault();
14525         
14526         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14527             return;
14528         }
14529         
14530         Roo.each(this.selectorEl.dom.files, function(file){    
14531             this.addFile(file);
14532         }, this);
14533          
14534     },
14535     
14536       
14537     
14538       
14539     
14540     addFile : function(file)
14541     {
14542            
14543         if(typeof(file) === 'string'){
14544             throw "Add file by name?"; // should not happen
14545             return;
14546         }
14547         
14548         if(!file || !this.urlAPI){
14549             return;
14550         }
14551         
14552         // file;
14553         // file.type;
14554         
14555         var _this = this;
14556         
14557         
14558         var url = _this.urlAPI.createObjectURL( file);
14559            
14560         this.addCard({
14561             id : Roo.bootstrap.form.CardUploader.ID--,
14562             is_uploaded : false,
14563             src : url,
14564             srcfile : file,
14565             title : file.name,
14566             mimetype : file.type,
14567             preview : false,
14568             is_deleted : 0
14569         });
14570         
14571     },
14572     
14573     /**
14574      * addCard - add an Attachment to the uploader
14575      * @param data - the data about the image to upload
14576      *
14577      * {
14578           id : 123
14579           title : "Title of file",
14580           is_uploaded : false,
14581           src : "http://.....",
14582           srcfile : { the File upload object },
14583           mimetype : file.type,
14584           preview : false,
14585           is_deleted : 0
14586           .. any other data...
14587         }
14588      *
14589      * 
14590     */
14591     
14592     addCard : function (data)
14593     {
14594         // hidden input element?
14595         // if the file is not an image...
14596         //then we need to use something other that and header_image
14597         var t = this;
14598         //   remove.....
14599         var footer = [
14600             {
14601                 xns : Roo.bootstrap,
14602                 xtype : 'CardFooter',
14603                  items: [
14604                     {
14605                         xns : Roo.bootstrap,
14606                         xtype : 'Element',
14607                         cls : 'd-flex',
14608                         items : [
14609                             
14610                             {
14611                                 xns : Roo.bootstrap,
14612                                 xtype : 'Button',
14613                                 html : String.format("<small>{0}</small>", data.title),
14614                                 cls : 'col-10 text-left',
14615                                 size: 'sm',
14616                                 weight: 'link',
14617                                 fa : 'download',
14618                                 listeners : {
14619                                     click : function() {
14620                                      
14621                                         t.fireEvent( "download", t, data );
14622                                     }
14623                                 }
14624                             },
14625                           
14626                             {
14627                                 xns : Roo.bootstrap,
14628                                 xtype : 'Button',
14629                                 style: 'max-height: 28px; ',
14630                                 size : 'sm',
14631                                 weight: 'danger',
14632                                 cls : 'col-2',
14633                                 fa : 'times',
14634                                 listeners : {
14635                                     click : function() {
14636                                         t.removeCard(data.id)
14637                                     }
14638                                 }
14639                             }
14640                         ]
14641                     }
14642                     
14643                 ] 
14644             }
14645             
14646         ];
14647         
14648         var cn = this.addxtype(
14649             {
14650                  
14651                 xns : Roo.bootstrap,
14652                 xtype : 'Card',
14653                 closeable : true,
14654                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14655                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14656                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14657                 data : data,
14658                 html : false,
14659                  
14660                 items : footer,
14661                 initEvents : function() {
14662                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14663                     var card = this;
14664                     this.imgEl = this.el.select('.card-img-top').first();
14665                     if (this.imgEl) {
14666                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14667                         this.imgEl.set({ 'pointer' : 'cursor' });
14668                                   
14669                     }
14670                     this.getCardFooter().addClass('p-1');
14671                     
14672                   
14673                 }
14674                 
14675             }
14676         );
14677         // dont' really need ot update items.
14678         // this.items.push(cn);
14679         this.fileCollection.add(cn);
14680         
14681         if (!data.srcfile) {
14682             this.updateInput();
14683             return;
14684         }
14685             
14686         var _t = this;
14687         var reader = new FileReader();
14688         reader.addEventListener("load", function() {  
14689             data.srcdata =  reader.result;
14690             _t.updateInput();
14691         });
14692         reader.readAsDataURL(data.srcfile);
14693         
14694         
14695         
14696     },
14697     removeCard : function(id)
14698     {
14699         
14700         var card  = this.fileCollection.get(id);
14701         card.data.is_deleted = 1;
14702         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14703         //this.fileCollection.remove(card);
14704         //this.items = this.items.filter(function(e) { return e != card });
14705         // dont' really need ot update items.
14706         card.el.dom.parentNode.removeChild(card.el.dom);
14707         this.updateInput();
14708
14709         
14710     },
14711     reset: function()
14712     {
14713         this.fileCollection.each(function(card) {
14714             if (card.el.dom && card.el.dom.parentNode) {
14715                 card.el.dom.parentNode.removeChild(card.el.dom);
14716             }
14717         });
14718         this.fileCollection.clear();
14719         this.updateInput();
14720     },
14721     
14722     updateInput : function()
14723     {
14724          var data = [];
14725         this.fileCollection.each(function(e) {
14726             data.push(e.data);
14727             
14728         });
14729         this.inputEl().dom.value = JSON.stringify(data);
14730         
14731         
14732         
14733     }
14734     
14735     
14736 });
14737
14738
14739 Roo.bootstrap.form.CardUploader.ID = -1;/*
14740  * Based on:
14741  * Ext JS Library 1.1.1
14742  * Copyright(c) 2006-2007, Ext JS, LLC.
14743  *
14744  * Originally Released Under LGPL - original licence link has changed is not relivant.
14745  *
14746  * Fork - LGPL
14747  * <script type="text/javascript">
14748  */
14749
14750
14751 /**
14752  * @class Roo.data.SortTypes
14753  * @static
14754  * Defines the default sorting (casting?) comparison functions used when sorting data.
14755  */
14756 Roo.data.SortTypes = {
14757     /**
14758      * Default sort that does nothing
14759      * @param {Mixed} s The value being converted
14760      * @return {Mixed} The comparison value
14761      */
14762     none : function(s){
14763         return s;
14764     },
14765     
14766     /**
14767      * The regular expression used to strip tags
14768      * @type {RegExp}
14769      * @property
14770      */
14771     stripTagsRE : /<\/?[^>]+>/gi,
14772     
14773     /**
14774      * Strips all HTML tags to sort on text only
14775      * @param {Mixed} s The value being converted
14776      * @return {String} The comparison value
14777      */
14778     asText : function(s){
14779         return String(s).replace(this.stripTagsRE, "");
14780     },
14781     
14782     /**
14783      * Strips all HTML tags to sort on text only - Case insensitive
14784      * @param {Mixed} s The value being converted
14785      * @return {String} The comparison value
14786      */
14787     asUCText : function(s){
14788         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14789     },
14790     
14791     /**
14792      * Case insensitive string
14793      * @param {Mixed} s The value being converted
14794      * @return {String} The comparison value
14795      */
14796     asUCString : function(s) {
14797         return String(s).toUpperCase();
14798     },
14799     
14800     /**
14801      * Date sorting
14802      * @param {Mixed} s The value being converted
14803      * @return {Number} The comparison value
14804      */
14805     asDate : function(s) {
14806         if(!s){
14807             return 0;
14808         }
14809         if(s instanceof Date){
14810             return s.getTime();
14811         }
14812         return Date.parse(String(s));
14813     },
14814     
14815     /**
14816      * Float sorting
14817      * @param {Mixed} s The value being converted
14818      * @return {Float} The comparison value
14819      */
14820     asFloat : function(s) {
14821         var val = parseFloat(String(s).replace(/,/g, ""));
14822         if(isNaN(val)) {
14823             val = 0;
14824         }
14825         return val;
14826     },
14827     
14828     /**
14829      * Integer sorting
14830      * @param {Mixed} s The value being converted
14831      * @return {Number} The comparison value
14832      */
14833     asInt : function(s) {
14834         var val = parseInt(String(s).replace(/,/g, ""));
14835         if(isNaN(val)) {
14836             val = 0;
14837         }
14838         return val;
14839     }
14840 };/*
14841  * Based on:
14842  * Ext JS Library 1.1.1
14843  * Copyright(c) 2006-2007, Ext JS, LLC.
14844  *
14845  * Originally Released Under LGPL - original licence link has changed is not relivant.
14846  *
14847  * Fork - LGPL
14848  * <script type="text/javascript">
14849  */
14850
14851 /**
14852 * @class Roo.data.Record
14853  * Instances of this class encapsulate both record <em>definition</em> information, and record
14854  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14855  * to access Records cached in an {@link Roo.data.Store} object.<br>
14856  * <p>
14857  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14858  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14859  * objects.<br>
14860  * <p>
14861  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14862  * @constructor
14863  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14864  * {@link #create}. The parameters are the same.
14865  * @param {Array} data An associative Array of data values keyed by the field name.
14866  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14867  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14868  * not specified an integer id is generated.
14869  */
14870 Roo.data.Record = function(data, id){
14871     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14872     this.data = data;
14873 };
14874
14875 /**
14876  * Generate a constructor for a specific record layout.
14877  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14878  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14879  * Each field definition object may contain the following properties: <ul>
14880  * <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,
14881  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14882  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14883  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14884  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14885  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14886  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14887  * this may be omitted.</p></li>
14888  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14889  * <ul><li>auto (Default, implies no conversion)</li>
14890  * <li>string</li>
14891  * <li>int</li>
14892  * <li>float</li>
14893  * <li>boolean</li>
14894  * <li>date</li></ul></p></li>
14895  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14896  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14897  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14898  * by the Reader into an object that will be stored in the Record. It is passed the
14899  * following parameters:<ul>
14900  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14901  * </ul></p></li>
14902  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14903  * </ul>
14904  * <br>usage:<br><pre><code>
14905 var TopicRecord = Roo.data.Record.create(
14906     {name: 'title', mapping: 'topic_title'},
14907     {name: 'author', mapping: 'username'},
14908     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14909     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14910     {name: 'lastPoster', mapping: 'user2'},
14911     {name: 'excerpt', mapping: 'post_text'}
14912 );
14913
14914 var myNewRecord = new TopicRecord({
14915     title: 'Do my job please',
14916     author: 'noobie',
14917     totalPosts: 1,
14918     lastPost: new Date(),
14919     lastPoster: 'Animal',
14920     excerpt: 'No way dude!'
14921 });
14922 myStore.add(myNewRecord);
14923 </code></pre>
14924  * @method create
14925  * @static
14926  */
14927 Roo.data.Record.create = function(o){
14928     var f = function(){
14929         f.superclass.constructor.apply(this, arguments);
14930     };
14931     Roo.extend(f, Roo.data.Record);
14932     var p = f.prototype;
14933     p.fields = new Roo.util.MixedCollection(false, function(field){
14934         return field.name;
14935     });
14936     for(var i = 0, len = o.length; i < len; i++){
14937         p.fields.add(new Roo.data.Field(o[i]));
14938     }
14939     f.getField = function(name){
14940         return p.fields.get(name);  
14941     };
14942     return f;
14943 };
14944
14945 Roo.data.Record.AUTO_ID = 1000;
14946 Roo.data.Record.EDIT = 'edit';
14947 Roo.data.Record.REJECT = 'reject';
14948 Roo.data.Record.COMMIT = 'commit';
14949
14950 Roo.data.Record.prototype = {
14951     /**
14952      * Readonly flag - true if this record has been modified.
14953      * @type Boolean
14954      */
14955     dirty : false,
14956     editing : false,
14957     error: null,
14958     modified: null,
14959
14960     // private
14961     join : function(store){
14962         this.store = store;
14963     },
14964
14965     /**
14966      * Set the named field to the specified value.
14967      * @param {String} name The name of the field to set.
14968      * @param {Object} value The value to set the field to.
14969      */
14970     set : function(name, value){
14971         if(this.data[name] == value){
14972             return;
14973         }
14974         this.dirty = true;
14975         if(!this.modified){
14976             this.modified = {};
14977         }
14978         if(typeof this.modified[name] == 'undefined'){
14979             this.modified[name] = this.data[name];
14980         }
14981         this.data[name] = value;
14982         if(!this.editing && this.store){
14983             this.store.afterEdit(this);
14984         }       
14985     },
14986
14987     /**
14988      * Get the value of the named field.
14989      * @param {String} name The name of the field to get the value of.
14990      * @return {Object} The value of the field.
14991      */
14992     get : function(name){
14993         return this.data[name]; 
14994     },
14995
14996     // private
14997     beginEdit : function(){
14998         this.editing = true;
14999         this.modified = {}; 
15000     },
15001
15002     // private
15003     cancelEdit : function(){
15004         this.editing = false;
15005         delete this.modified;
15006     },
15007
15008     // private
15009     endEdit : function(){
15010         this.editing = false;
15011         if(this.dirty && this.store){
15012             this.store.afterEdit(this);
15013         }
15014     },
15015
15016     /**
15017      * Usually called by the {@link Roo.data.Store} which owns the Record.
15018      * Rejects all changes made to the Record since either creation, or the last commit operation.
15019      * Modified fields are reverted to their original values.
15020      * <p>
15021      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15022      * of reject operations.
15023      */
15024     reject : function(){
15025         var m = this.modified;
15026         for(var n in m){
15027             if(typeof m[n] != "function"){
15028                 this.data[n] = m[n];
15029             }
15030         }
15031         this.dirty = false;
15032         delete this.modified;
15033         this.editing = false;
15034         if(this.store){
15035             this.store.afterReject(this);
15036         }
15037     },
15038
15039     /**
15040      * Usually called by the {@link Roo.data.Store} which owns the Record.
15041      * Commits all changes made to the Record since either creation, or the last commit operation.
15042      * <p>
15043      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15044      * of commit operations.
15045      */
15046     commit : function(){
15047         this.dirty = false;
15048         delete this.modified;
15049         this.editing = false;
15050         if(this.store){
15051             this.store.afterCommit(this);
15052         }
15053     },
15054
15055     // private
15056     hasError : function(){
15057         return this.error != null;
15058     },
15059
15060     // private
15061     clearError : function(){
15062         this.error = null;
15063     },
15064
15065     /**
15066      * Creates a copy of this record.
15067      * @param {String} id (optional) A new record id if you don't want to use this record's id
15068      * @return {Record}
15069      */
15070     copy : function(newId) {
15071         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15072     }
15073 };/*
15074  * Based on:
15075  * Ext JS Library 1.1.1
15076  * Copyright(c) 2006-2007, Ext JS, LLC.
15077  *
15078  * Originally Released Under LGPL - original licence link has changed is not relivant.
15079  *
15080  * Fork - LGPL
15081  * <script type="text/javascript">
15082  */
15083
15084
15085
15086 /**
15087  * @class Roo.data.Store
15088  * @extends Roo.util.Observable
15089  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15090  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15091  * <p>
15092  * 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
15093  * has no knowledge of the format of the data returned by the Proxy.<br>
15094  * <p>
15095  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15096  * instances from the data object. These records are cached and made available through accessor functions.
15097  * @constructor
15098  * Creates a new Store.
15099  * @param {Object} config A config object containing the objects needed for the Store to access data,
15100  * and read the data into Records.
15101  */
15102 Roo.data.Store = function(config){
15103     this.data = new Roo.util.MixedCollection(false);
15104     this.data.getKey = function(o){
15105         return o.id;
15106     };
15107     this.baseParams = {};
15108     // private
15109     this.paramNames = {
15110         "start" : "start",
15111         "limit" : "limit",
15112         "sort" : "sort",
15113         "dir" : "dir",
15114         "multisort" : "_multisort"
15115     };
15116
15117     if(config && config.data){
15118         this.inlineData = config.data;
15119         delete config.data;
15120     }
15121
15122     Roo.apply(this, config);
15123     
15124     if(this.reader){ // reader passed
15125         this.reader = Roo.factory(this.reader, Roo.data);
15126         this.reader.xmodule = this.xmodule || false;
15127         if(!this.recordType){
15128             this.recordType = this.reader.recordType;
15129         }
15130         if(this.reader.onMetaChange){
15131             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15132         }
15133     }
15134
15135     if(this.recordType){
15136         this.fields = this.recordType.prototype.fields;
15137     }
15138     this.modified = [];
15139
15140     this.addEvents({
15141         /**
15142          * @event datachanged
15143          * Fires when the data cache has changed, and a widget which is using this Store
15144          * as a Record cache should refresh its view.
15145          * @param {Store} this
15146          */
15147         datachanged : true,
15148         /**
15149          * @event metachange
15150          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15151          * @param {Store} this
15152          * @param {Object} meta The JSON metadata
15153          */
15154         metachange : true,
15155         /**
15156          * @event add
15157          * Fires when Records have been added to the Store
15158          * @param {Store} this
15159          * @param {Roo.data.Record[]} records The array of Records added
15160          * @param {Number} index The index at which the record(s) were added
15161          */
15162         add : true,
15163         /**
15164          * @event remove
15165          * Fires when a Record has been removed from the Store
15166          * @param {Store} this
15167          * @param {Roo.data.Record} record The Record that was removed
15168          * @param {Number} index The index at which the record was removed
15169          */
15170         remove : true,
15171         /**
15172          * @event update
15173          * Fires when a Record has been updated
15174          * @param {Store} this
15175          * @param {Roo.data.Record} record The Record that was updated
15176          * @param {String} operation The update operation being performed.  Value may be one of:
15177          * <pre><code>
15178  Roo.data.Record.EDIT
15179  Roo.data.Record.REJECT
15180  Roo.data.Record.COMMIT
15181          * </code></pre>
15182          */
15183         update : true,
15184         /**
15185          * @event clear
15186          * Fires when the data cache has been cleared.
15187          * @param {Store} this
15188          */
15189         clear : true,
15190         /**
15191          * @event beforeload
15192          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15193          * the load action will be canceled.
15194          * @param {Store} this
15195          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15196          */
15197         beforeload : true,
15198         /**
15199          * @event beforeloadadd
15200          * Fires after a new set of Records has been loaded.
15201          * @param {Store} this
15202          * @param {Roo.data.Record[]} records The Records that were loaded
15203          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15204          */
15205         beforeloadadd : true,
15206         /**
15207          * @event load
15208          * Fires after a new set of Records has been loaded, before they are added to the store.
15209          * @param {Store} this
15210          * @param {Roo.data.Record[]} records The Records that were loaded
15211          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15212          * @params {Object} return from reader
15213          */
15214         load : true,
15215         /**
15216          * @event loadexception
15217          * Fires if an exception occurs in the Proxy during loading.
15218          * Called with the signature of the Proxy's "loadexception" event.
15219          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15220          * 
15221          * @param {Proxy} 
15222          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15223          * @param {Object} load options 
15224          * @param {Object} jsonData from your request (normally this contains the Exception)
15225          */
15226         loadexception : true
15227     });
15228     
15229     if(this.proxy){
15230         this.proxy = Roo.factory(this.proxy, Roo.data);
15231         this.proxy.xmodule = this.xmodule || false;
15232         this.relayEvents(this.proxy,  ["loadexception"]);
15233     }
15234     this.sortToggle = {};
15235     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15236
15237     Roo.data.Store.superclass.constructor.call(this);
15238
15239     if(this.inlineData){
15240         this.loadData(this.inlineData);
15241         delete this.inlineData;
15242     }
15243 };
15244
15245 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15246      /**
15247     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15248     * without a remote query - used by combo/forms at present.
15249     */
15250     
15251     /**
15252     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15253     */
15254     /**
15255     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15256     */
15257     /**
15258     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15259     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15260     */
15261     /**
15262     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15263     * on any HTTP request
15264     */
15265     /**
15266     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15267     */
15268     /**
15269     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15270     */
15271     multiSort: false,
15272     /**
15273     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15274     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15275     */
15276     remoteSort : false,
15277
15278     /**
15279     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15280      * loaded or when a record is removed. (defaults to false).
15281     */
15282     pruneModifiedRecords : false,
15283
15284     // private
15285     lastOptions : null,
15286
15287     /**
15288      * Add Records to the Store and fires the add event.
15289      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15290      */
15291     add : function(records){
15292         records = [].concat(records);
15293         for(var i = 0, len = records.length; i < len; i++){
15294             records[i].join(this);
15295         }
15296         var index = this.data.length;
15297         this.data.addAll(records);
15298         this.fireEvent("add", this, records, index);
15299     },
15300
15301     /**
15302      * Remove a Record from the Store and fires the remove event.
15303      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15304      */
15305     remove : function(record){
15306         var index = this.data.indexOf(record);
15307         this.data.removeAt(index);
15308  
15309         if(this.pruneModifiedRecords){
15310             this.modified.remove(record);
15311         }
15312         this.fireEvent("remove", this, record, index);
15313     },
15314
15315     /**
15316      * Remove all Records from the Store and fires the clear event.
15317      */
15318     removeAll : function(){
15319         this.data.clear();
15320         if(this.pruneModifiedRecords){
15321             this.modified = [];
15322         }
15323         this.fireEvent("clear", this);
15324     },
15325
15326     /**
15327      * Inserts Records to the Store at the given index and fires the add event.
15328      * @param {Number} index The start index at which to insert the passed Records.
15329      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15330      */
15331     insert : function(index, records){
15332         records = [].concat(records);
15333         for(var i = 0, len = records.length; i < len; i++){
15334             this.data.insert(index, records[i]);
15335             records[i].join(this);
15336         }
15337         this.fireEvent("add", this, records, index);
15338     },
15339
15340     /**
15341      * Get the index within the cache of the passed Record.
15342      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15343      * @return {Number} The index of the passed Record. Returns -1 if not found.
15344      */
15345     indexOf : function(record){
15346         return this.data.indexOf(record);
15347     },
15348
15349     /**
15350      * Get the index within the cache of the Record with the passed id.
15351      * @param {String} id The id of the Record to find.
15352      * @return {Number} The index of the Record. Returns -1 if not found.
15353      */
15354     indexOfId : function(id){
15355         return this.data.indexOfKey(id);
15356     },
15357
15358     /**
15359      * Get the Record with the specified id.
15360      * @param {String} id The id of the Record to find.
15361      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15362      */
15363     getById : function(id){
15364         return this.data.key(id);
15365     },
15366
15367     /**
15368      * Get the Record at the specified index.
15369      * @param {Number} index The index of the Record to find.
15370      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15371      */
15372     getAt : function(index){
15373         return this.data.itemAt(index);
15374     },
15375
15376     /**
15377      * Returns a range of Records between specified indices.
15378      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15379      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15380      * @return {Roo.data.Record[]} An array of Records
15381      */
15382     getRange : function(start, end){
15383         return this.data.getRange(start, end);
15384     },
15385
15386     // private
15387     storeOptions : function(o){
15388         o = Roo.apply({}, o);
15389         delete o.callback;
15390         delete o.scope;
15391         this.lastOptions = o;
15392     },
15393
15394     /**
15395      * Loads the Record cache from the configured Proxy using the configured Reader.
15396      * <p>
15397      * If using remote paging, then the first load call must specify the <em>start</em>
15398      * and <em>limit</em> properties in the options.params property to establish the initial
15399      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15400      * <p>
15401      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15402      * and this call will return before the new data has been loaded. Perform any post-processing
15403      * in a callback function, or in a "load" event handler.</strong>
15404      * <p>
15405      * @param {Object} options An object containing properties which control loading options:<ul>
15406      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15407      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15408      * <pre>
15409                 {
15410                     data : data,  // array of key=>value data like JsonReader
15411                     total : data.length,
15412                     success : true
15413                     
15414                 }
15415         </pre>
15416             }.</li>
15417      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15418      * passed the following arguments:<ul>
15419      * <li>r : Roo.data.Record[]</li>
15420      * <li>options: Options object from the load call</li>
15421      * <li>success: Boolean success indicator</li></ul></li>
15422      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15423      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15424      * </ul>
15425      */
15426     load : function(options){
15427         options = options || {};
15428         if(this.fireEvent("beforeload", this, options) !== false){
15429             this.storeOptions(options);
15430             var p = Roo.apply(options.params || {}, this.baseParams);
15431             // if meta was not loaded from remote source.. try requesting it.
15432             if (!this.reader.metaFromRemote) {
15433                 p._requestMeta = 1;
15434             }
15435             if(this.sortInfo && this.remoteSort){
15436                 var pn = this.paramNames;
15437                 p[pn["sort"]] = this.sortInfo.field;
15438                 p[pn["dir"]] = this.sortInfo.direction;
15439             }
15440             if (this.multiSort) {
15441                 var pn = this.paramNames;
15442                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15443             }
15444             
15445             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15446         }
15447     },
15448
15449     /**
15450      * Reloads the Record cache from the configured Proxy using the configured Reader and
15451      * the options from the last load operation performed.
15452      * @param {Object} options (optional) An object containing properties which may override the options
15453      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15454      * the most recently used options are reused).
15455      */
15456     reload : function(options){
15457         this.load(Roo.applyIf(options||{}, this.lastOptions));
15458     },
15459
15460     // private
15461     // Called as a callback by the Reader during a load operation.
15462     loadRecords : function(o, options, success){
15463          
15464         if(!o){
15465             if(success !== false){
15466                 this.fireEvent("load", this, [], options, o);
15467             }
15468             if(options.callback){
15469                 options.callback.call(options.scope || this, [], options, false);
15470             }
15471             return;
15472         }
15473         // if data returned failure - throw an exception.
15474         if (o.success === false) {
15475             // show a message if no listener is registered.
15476             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15477                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15478             }
15479             // loadmask wil be hooked into this..
15480             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15481             return;
15482         }
15483         var r = o.records, t = o.totalRecords || r.length;
15484         
15485         this.fireEvent("beforeloadadd", this, r, options, o);
15486         
15487         if(!options || options.add !== true){
15488             if(this.pruneModifiedRecords){
15489                 this.modified = [];
15490             }
15491             for(var i = 0, len = r.length; i < len; i++){
15492                 r[i].join(this);
15493             }
15494             if(this.snapshot){
15495                 this.data = this.snapshot;
15496                 delete this.snapshot;
15497             }
15498             this.data.clear();
15499             this.data.addAll(r);
15500             this.totalLength = t;
15501             this.applySort();
15502             this.fireEvent("datachanged", this);
15503         }else{
15504             this.totalLength = Math.max(t, this.data.length+r.length);
15505             this.add(r);
15506         }
15507         
15508         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15509                 
15510             var e = new Roo.data.Record({});
15511
15512             e.set(this.parent.displayField, this.parent.emptyTitle);
15513             e.set(this.parent.valueField, '');
15514
15515             this.insert(0, e);
15516         }
15517             
15518         this.fireEvent("load", this, r, options, o);
15519         if(options.callback){
15520             options.callback.call(options.scope || this, r, options, true);
15521         }
15522     },
15523
15524
15525     /**
15526      * Loads data from a passed data block. A Reader which understands the format of the data
15527      * must have been configured in the constructor.
15528      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15529      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15530      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15531      */
15532     loadData : function(o, append){
15533         var r = this.reader.readRecords(o);
15534         this.loadRecords(r, {add: append}, true);
15535     },
15536     
15537      /**
15538      * using 'cn' the nested child reader read the child array into it's child stores.
15539      * @param {Object} rec The record with a 'children array
15540      */
15541     loadDataFromChildren : function(rec)
15542     {
15543         this.loadData(this.reader.toLoadData(rec));
15544     },
15545     
15546
15547     /**
15548      * Gets the number of cached records.
15549      * <p>
15550      * <em>If using paging, this may not be the total size of the dataset. If the data object
15551      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15552      * the data set size</em>
15553      */
15554     getCount : function(){
15555         return this.data.length || 0;
15556     },
15557
15558     /**
15559      * Gets the total number of records in the dataset as returned by the server.
15560      * <p>
15561      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15562      * the dataset size</em>
15563      */
15564     getTotalCount : function(){
15565         return this.totalLength || 0;
15566     },
15567
15568     /**
15569      * Returns the sort state of the Store as an object with two properties:
15570      * <pre><code>
15571  field {String} The name of the field by which the Records are sorted
15572  direction {String} The sort order, "ASC" or "DESC"
15573      * </code></pre>
15574      */
15575     getSortState : function(){
15576         return this.sortInfo;
15577     },
15578
15579     // private
15580     applySort : function(){
15581         if(this.sortInfo && !this.remoteSort){
15582             var s = this.sortInfo, f = s.field;
15583             var st = this.fields.get(f).sortType;
15584             var fn = function(r1, r2){
15585                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15586                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15587             };
15588             this.data.sort(s.direction, fn);
15589             if(this.snapshot && this.snapshot != this.data){
15590                 this.snapshot.sort(s.direction, fn);
15591             }
15592         }
15593     },
15594
15595     /**
15596      * Sets the default sort column and order to be used by the next load operation.
15597      * @param {String} fieldName The name of the field to sort by.
15598      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15599      */
15600     setDefaultSort : function(field, dir){
15601         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15602     },
15603
15604     /**
15605      * Sort the Records.
15606      * If remote sorting is used, the sort is performed on the server, and the cache is
15607      * reloaded. If local sorting is used, the cache is sorted internally.
15608      * @param {String} fieldName The name of the field to sort by.
15609      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15610      */
15611     sort : function(fieldName, dir){
15612         var f = this.fields.get(fieldName);
15613         if(!dir){
15614             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15615             
15616             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15617                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15618             }else{
15619                 dir = f.sortDir;
15620             }
15621         }
15622         this.sortToggle[f.name] = dir;
15623         this.sortInfo = {field: f.name, direction: dir};
15624         if(!this.remoteSort){
15625             this.applySort();
15626             this.fireEvent("datachanged", this);
15627         }else{
15628             this.load(this.lastOptions);
15629         }
15630     },
15631
15632     /**
15633      * Calls the specified function for each of the Records in the cache.
15634      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15635      * Returning <em>false</em> aborts and exits the iteration.
15636      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15637      */
15638     each : function(fn, scope){
15639         this.data.each(fn, scope);
15640     },
15641
15642     /**
15643      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15644      * (e.g., during paging).
15645      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15646      */
15647     getModifiedRecords : function(){
15648         return this.modified;
15649     },
15650
15651     // private
15652     createFilterFn : function(property, value, anyMatch){
15653         if(!value.exec){ // not a regex
15654             value = String(value);
15655             if(value.length == 0){
15656                 return false;
15657             }
15658             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15659         }
15660         return function(r){
15661             return value.test(r.data[property]);
15662         };
15663     },
15664
15665     /**
15666      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15667      * @param {String} property A field on your records
15668      * @param {Number} start The record index to start at (defaults to 0)
15669      * @param {Number} end The last record index to include (defaults to length - 1)
15670      * @return {Number} The sum
15671      */
15672     sum : function(property, start, end){
15673         var rs = this.data.items, v = 0;
15674         start = start || 0;
15675         end = (end || end === 0) ? end : rs.length-1;
15676
15677         for(var i = start; i <= end; i++){
15678             v += (rs[i].data[property] || 0);
15679         }
15680         return v;
15681     },
15682
15683     /**
15684      * Filter the records by a specified property.
15685      * @param {String} field A field on your records
15686      * @param {String/RegExp} value Either a string that the field
15687      * should start with or a RegExp to test against the field
15688      * @param {Boolean} anyMatch True to match any part not just the beginning
15689      */
15690     filter : function(property, value, anyMatch){
15691         var fn = this.createFilterFn(property, value, anyMatch);
15692         return fn ? this.filterBy(fn) : this.clearFilter();
15693     },
15694
15695     /**
15696      * Filter by a function. The specified function will be called with each
15697      * record in this data source. If the function returns true the record is included,
15698      * otherwise it is filtered.
15699      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15700      * @param {Object} scope (optional) The scope of the function (defaults to this)
15701      */
15702     filterBy : function(fn, scope){
15703         this.snapshot = this.snapshot || this.data;
15704         this.data = this.queryBy(fn, scope||this);
15705         this.fireEvent("datachanged", this);
15706     },
15707
15708     /**
15709      * Query the records by a specified property.
15710      * @param {String} field A field on your records
15711      * @param {String/RegExp} value Either a string that the field
15712      * should start with or a RegExp to test against the field
15713      * @param {Boolean} anyMatch True to match any part not just the beginning
15714      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15715      */
15716     query : function(property, value, anyMatch){
15717         var fn = this.createFilterFn(property, value, anyMatch);
15718         return fn ? this.queryBy(fn) : this.data.clone();
15719     },
15720
15721     /**
15722      * Query by a function. The specified function will be called with each
15723      * record in this data source. If the function returns true the record is included
15724      * in the results.
15725      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15726      * @param {Object} scope (optional) The scope of the function (defaults to this)
15727       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15728      **/
15729     queryBy : function(fn, scope){
15730         var data = this.snapshot || this.data;
15731         return data.filterBy(fn, scope||this);
15732     },
15733
15734     /**
15735      * Collects unique values for a particular dataIndex from this store.
15736      * @param {String} dataIndex The property to collect
15737      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15738      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15739      * @return {Array} An array of the unique values
15740      **/
15741     collect : function(dataIndex, allowNull, bypassFilter){
15742         var d = (bypassFilter === true && this.snapshot) ?
15743                 this.snapshot.items : this.data.items;
15744         var v, sv, r = [], l = {};
15745         for(var i = 0, len = d.length; i < len; i++){
15746             v = d[i].data[dataIndex];
15747             sv = String(v);
15748             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15749                 l[sv] = true;
15750                 r[r.length] = v;
15751             }
15752         }
15753         return r;
15754     },
15755
15756     /**
15757      * Revert to a view of the Record cache with no filtering applied.
15758      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15759      */
15760     clearFilter : function(suppressEvent){
15761         if(this.snapshot && this.snapshot != this.data){
15762             this.data = this.snapshot;
15763             delete this.snapshot;
15764             if(suppressEvent !== true){
15765                 this.fireEvent("datachanged", this);
15766             }
15767         }
15768     },
15769
15770     // private
15771     afterEdit : function(record){
15772         if(this.modified.indexOf(record) == -1){
15773             this.modified.push(record);
15774         }
15775         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15776     },
15777     
15778     // private
15779     afterReject : function(record){
15780         this.modified.remove(record);
15781         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15782     },
15783
15784     // private
15785     afterCommit : function(record){
15786         this.modified.remove(record);
15787         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15788     },
15789
15790     /**
15791      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15792      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15793      */
15794     commitChanges : function(){
15795         var m = this.modified.slice(0);
15796         this.modified = [];
15797         for(var i = 0, len = m.length; i < len; i++){
15798             m[i].commit();
15799         }
15800     },
15801
15802     /**
15803      * Cancel outstanding changes on all changed records.
15804      */
15805     rejectChanges : function(){
15806         var m = this.modified.slice(0);
15807         this.modified = [];
15808         for(var i = 0, len = m.length; i < len; i++){
15809             m[i].reject();
15810         }
15811     },
15812
15813     onMetaChange : function(meta, rtype, o){
15814         this.recordType = rtype;
15815         this.fields = rtype.prototype.fields;
15816         delete this.snapshot;
15817         this.sortInfo = meta.sortInfo || this.sortInfo;
15818         this.modified = [];
15819         this.fireEvent('metachange', this, this.reader.meta);
15820     },
15821     
15822     moveIndex : function(data, type)
15823     {
15824         var index = this.indexOf(data);
15825         
15826         var newIndex = index + type;
15827         
15828         this.remove(data);
15829         
15830         this.insert(newIndex, data);
15831         
15832     }
15833 });/*
15834  * Based on:
15835  * Ext JS Library 1.1.1
15836  * Copyright(c) 2006-2007, Ext JS, LLC.
15837  *
15838  * Originally Released Under LGPL - original licence link has changed is not relivant.
15839  *
15840  * Fork - LGPL
15841  * <script type="text/javascript">
15842  */
15843
15844 /**
15845  * @class Roo.data.SimpleStore
15846  * @extends Roo.data.Store
15847  * Small helper class to make creating Stores from Array data easier.
15848  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15849  * @cfg {Array} fields An array of field definition objects, or field name strings.
15850  * @cfg {Object} an existing reader (eg. copied from another store)
15851  * @cfg {Array} data The multi-dimensional array of data
15852  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15853  * @cfg {Roo.data.Reader} reader  [not-required] 
15854  * @constructor
15855  * @param {Object} config
15856  */
15857 Roo.data.SimpleStore = function(config)
15858 {
15859     Roo.data.SimpleStore.superclass.constructor.call(this, {
15860         isLocal : true,
15861         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15862                 id: config.id
15863             },
15864             Roo.data.Record.create(config.fields)
15865         ),
15866         proxy : new Roo.data.MemoryProxy(config.data)
15867     });
15868     this.load();
15869 };
15870 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15871  * Based on:
15872  * Ext JS Library 1.1.1
15873  * Copyright(c) 2006-2007, Ext JS, LLC.
15874  *
15875  * Originally Released Under LGPL - original licence link has changed is not relivant.
15876  *
15877  * Fork - LGPL
15878  * <script type="text/javascript">
15879  */
15880
15881 /**
15882 /**
15883  * @extends Roo.data.Store
15884  * @class Roo.data.JsonStore
15885  * Small helper class to make creating Stores for JSON data easier. <br/>
15886 <pre><code>
15887 var store = new Roo.data.JsonStore({
15888     url: 'get-images.php',
15889     root: 'images',
15890     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15891 });
15892 </code></pre>
15893  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15894  * JsonReader and HttpProxy (unless inline data is provided).</b>
15895  * @cfg {Array} fields An array of field definition objects, or field name strings.
15896  * @constructor
15897  * @param {Object} config
15898  */
15899 Roo.data.JsonStore = function(c){
15900     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15901         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15902         reader: new Roo.data.JsonReader(c, c.fields)
15903     }));
15904 };
15905 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15906  * Based on:
15907  * Ext JS Library 1.1.1
15908  * Copyright(c) 2006-2007, Ext JS, LLC.
15909  *
15910  * Originally Released Under LGPL - original licence link has changed is not relivant.
15911  *
15912  * Fork - LGPL
15913  * <script type="text/javascript">
15914  */
15915
15916  
15917 Roo.data.Field = function(config){
15918     if(typeof config == "string"){
15919         config = {name: config};
15920     }
15921     Roo.apply(this, config);
15922     
15923     if(!this.type){
15924         this.type = "auto";
15925     }
15926     
15927     var st = Roo.data.SortTypes;
15928     // named sortTypes are supported, here we look them up
15929     if(typeof this.sortType == "string"){
15930         this.sortType = st[this.sortType];
15931     }
15932     
15933     // set default sortType for strings and dates
15934     if(!this.sortType){
15935         switch(this.type){
15936             case "string":
15937                 this.sortType = st.asUCString;
15938                 break;
15939             case "date":
15940                 this.sortType = st.asDate;
15941                 break;
15942             default:
15943                 this.sortType = st.none;
15944         }
15945     }
15946
15947     // define once
15948     var stripRe = /[\$,%]/g;
15949
15950     // prebuilt conversion function for this field, instead of
15951     // switching every time we're reading a value
15952     if(!this.convert){
15953         var cv, dateFormat = this.dateFormat;
15954         switch(this.type){
15955             case "":
15956             case "auto":
15957             case undefined:
15958                 cv = function(v){ return v; };
15959                 break;
15960             case "string":
15961                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15962                 break;
15963             case "int":
15964                 cv = function(v){
15965                     return v !== undefined && v !== null && v !== '' ?
15966                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15967                     };
15968                 break;
15969             case "float":
15970                 cv = function(v){
15971                     return v !== undefined && v !== null && v !== '' ?
15972                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15973                     };
15974                 break;
15975             case "bool":
15976             case "boolean":
15977                 cv = function(v){ return v === true || v === "true" || v == 1; };
15978                 break;
15979             case "date":
15980                 cv = function(v){
15981                     if(!v){
15982                         return '';
15983                     }
15984                     if(v instanceof Date){
15985                         return v;
15986                     }
15987                     if(dateFormat){
15988                         if(dateFormat == "timestamp"){
15989                             return new Date(v*1000);
15990                         }
15991                         return Date.parseDate(v, dateFormat);
15992                     }
15993                     var parsed = Date.parse(v);
15994                     return parsed ? new Date(parsed) : null;
15995                 };
15996              break;
15997             
15998         }
15999         this.convert = cv;
16000     }
16001 };
16002
16003 Roo.data.Field.prototype = {
16004     dateFormat: null,
16005     defaultValue: "",
16006     mapping: null,
16007     sortType : null,
16008     sortDir : "ASC"
16009 };/*
16010  * Based on:
16011  * Ext JS Library 1.1.1
16012  * Copyright(c) 2006-2007, Ext JS, LLC.
16013  *
16014  * Originally Released Under LGPL - original licence link has changed is not relivant.
16015  *
16016  * Fork - LGPL
16017  * <script type="text/javascript">
16018  */
16019  
16020 // Base class for reading structured data from a data source.  This class is intended to be
16021 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
16022
16023 /**
16024  * @class Roo.data.DataReader
16025  * @abstract
16026  * Base class for reading structured data from a data source.  This class is intended to be
16027  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
16028  */
16029
16030 Roo.data.DataReader = function(meta, recordType){
16031     
16032     this.meta = meta;
16033     
16034     this.recordType = recordType instanceof Array ? 
16035         Roo.data.Record.create(recordType) : recordType;
16036 };
16037
16038 Roo.data.DataReader.prototype = {
16039     
16040     
16041     readerType : 'Data',
16042      /**
16043      * Create an empty record
16044      * @param {Object} data (optional) - overlay some values
16045      * @return {Roo.data.Record} record created.
16046      */
16047     newRow :  function(d) {
16048         var da =  {};
16049         this.recordType.prototype.fields.each(function(c) {
16050             switch( c.type) {
16051                 case 'int' : da[c.name] = 0; break;
16052                 case 'date' : da[c.name] = new Date(); break;
16053                 case 'float' : da[c.name] = 0.0; break;
16054                 case 'boolean' : da[c.name] = false; break;
16055                 default : da[c.name] = ""; break;
16056             }
16057             
16058         });
16059         return new this.recordType(Roo.apply(da, d));
16060     }
16061     
16062     
16063 };/*
16064  * Based on:
16065  * Ext JS Library 1.1.1
16066  * Copyright(c) 2006-2007, Ext JS, LLC.
16067  *
16068  * Originally Released Under LGPL - original licence link has changed is not relivant.
16069  *
16070  * Fork - LGPL
16071  * <script type="text/javascript">
16072  */
16073
16074 /**
16075  * @class Roo.data.DataProxy
16076  * @extends Roo.util.Observable
16077  * @abstract
16078  * This class is an abstract base class for implementations which provide retrieval of
16079  * unformatted data objects.<br>
16080  * <p>
16081  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16082  * (of the appropriate type which knows how to parse the data object) to provide a block of
16083  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16084  * <p>
16085  * Custom implementations must implement the load method as described in
16086  * {@link Roo.data.HttpProxy#load}.
16087  */
16088 Roo.data.DataProxy = function(){
16089     this.addEvents({
16090         /**
16091          * @event beforeload
16092          * Fires before a network request is made to retrieve a data object.
16093          * @param {Object} This DataProxy object.
16094          * @param {Object} params The params parameter to the load function.
16095          */
16096         beforeload : true,
16097         /**
16098          * @event load
16099          * Fires before the load method's callback is called.
16100          * @param {Object} This DataProxy object.
16101          * @param {Object} o The data object.
16102          * @param {Object} arg The callback argument object passed to the load function.
16103          */
16104         load : true,
16105         /**
16106          * @event loadexception
16107          * Fires if an Exception occurs during data retrieval.
16108          * @param {Object} This DataProxy object.
16109          * @param {Object} o The data object.
16110          * @param {Object} arg The callback argument object passed to the load function.
16111          * @param {Object} e The Exception.
16112          */
16113         loadexception : true
16114     });
16115     Roo.data.DataProxy.superclass.constructor.call(this);
16116 };
16117
16118 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16119
16120     /**
16121      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16122      */
16123 /*
16124  * Based on:
16125  * Ext JS Library 1.1.1
16126  * Copyright(c) 2006-2007, Ext JS, LLC.
16127  *
16128  * Originally Released Under LGPL - original licence link has changed is not relivant.
16129  *
16130  * Fork - LGPL
16131  * <script type="text/javascript">
16132  */
16133 /**
16134  * @class Roo.data.MemoryProxy
16135  * @extends Roo.data.DataProxy
16136  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16137  * to the Reader when its load method is called.
16138  * @constructor
16139  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16140  */
16141 Roo.data.MemoryProxy = function(config){
16142     var data = config;
16143     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16144         data = config.data;
16145     }
16146     Roo.data.MemoryProxy.superclass.constructor.call(this);
16147     this.data = data;
16148 };
16149
16150 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16151     
16152     /**
16153      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16154      */
16155     /**
16156      * Load data from the requested source (in this case an in-memory
16157      * data object passed to the constructor), read the data object into
16158      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16159      * process that block using the passed callback.
16160      * @param {Object} params This parameter is not used by the MemoryProxy class.
16161      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16162      * object into a block of Roo.data.Records.
16163      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16164      * The function must be passed <ul>
16165      * <li>The Record block object</li>
16166      * <li>The "arg" argument from the load function</li>
16167      * <li>A boolean success indicator</li>
16168      * </ul>
16169      * @param {Object} scope The scope in which to call the callback
16170      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16171      */
16172     load : function(params, reader, callback, scope, arg){
16173         params = params || {};
16174         var result;
16175         try {
16176             result = reader.readRecords(params.data ? params.data :this.data);
16177         }catch(e){
16178             this.fireEvent("loadexception", this, arg, null, e);
16179             callback.call(scope, null, arg, false);
16180             return;
16181         }
16182         callback.call(scope, result, arg, true);
16183     },
16184     
16185     // private
16186     update : function(params, records){
16187         
16188     }
16189 });/*
16190  * Based on:
16191  * Ext JS Library 1.1.1
16192  * Copyright(c) 2006-2007, Ext JS, LLC.
16193  *
16194  * Originally Released Under LGPL - original licence link has changed is not relivant.
16195  *
16196  * Fork - LGPL
16197  * <script type="text/javascript">
16198  */
16199 /**
16200  * @class Roo.data.HttpProxy
16201  * @extends Roo.data.DataProxy
16202  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16203  * configured to reference a certain URL.<br><br>
16204  * <p>
16205  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16206  * from which the running page was served.<br><br>
16207  * <p>
16208  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16209  * <p>
16210  * Be aware that to enable the browser to parse an XML document, the server must set
16211  * the Content-Type header in the HTTP response to "text/xml".
16212  * @constructor
16213  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16214  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16215  * will be used to make the request.
16216  */
16217 Roo.data.HttpProxy = function(conn){
16218     Roo.data.HttpProxy.superclass.constructor.call(this);
16219     // is conn a conn config or a real conn?
16220     this.conn = conn;
16221     this.useAjax = !conn || !conn.events;
16222   
16223 };
16224
16225 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16226     // thse are take from connection...
16227     
16228     /**
16229      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16230      */
16231     /**
16232      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16233      * extra parameters to each request made by this object. (defaults to undefined)
16234      */
16235     /**
16236      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16237      *  to each request made by this object. (defaults to undefined)
16238      */
16239     /**
16240      * @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)
16241      */
16242     /**
16243      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16244      */
16245      /**
16246      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16247      * @type Boolean
16248      */
16249   
16250
16251     /**
16252      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16253      * @type Boolean
16254      */
16255     /**
16256      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16257      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16258      * a finer-grained basis than the DataProxy events.
16259      */
16260     getConnection : function(){
16261         return this.useAjax ? Roo.Ajax : this.conn;
16262     },
16263
16264     /**
16265      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16266      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16267      * process that block using the passed callback.
16268      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16269      * for the request to the remote server.
16270      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16271      * object into a block of Roo.data.Records.
16272      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16273      * The function must be passed <ul>
16274      * <li>The Record block object</li>
16275      * <li>The "arg" argument from the load function</li>
16276      * <li>A boolean success indicator</li>
16277      * </ul>
16278      * @param {Object} scope The scope in which to call the callback
16279      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16280      */
16281     load : function(params, reader, callback, scope, arg){
16282         if(this.fireEvent("beforeload", this, params) !== false){
16283             var  o = {
16284                 params : params || {},
16285                 request: {
16286                     callback : callback,
16287                     scope : scope,
16288                     arg : arg
16289                 },
16290                 reader: reader,
16291                 callback : this.loadResponse,
16292                 scope: this
16293             };
16294             if(this.useAjax){
16295                 Roo.applyIf(o, this.conn);
16296                 if(this.activeRequest){
16297                     Roo.Ajax.abort(this.activeRequest);
16298                 }
16299                 this.activeRequest = Roo.Ajax.request(o);
16300             }else{
16301                 this.conn.request(o);
16302             }
16303         }else{
16304             callback.call(scope||this, null, arg, false);
16305         }
16306     },
16307
16308     // private
16309     loadResponse : function(o, success, response){
16310         delete this.activeRequest;
16311         if(!success){
16312             this.fireEvent("loadexception", this, o, response);
16313             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16314             return;
16315         }
16316         var result;
16317         try {
16318             result = o.reader.read(response);
16319         }catch(e){
16320             o.success = false;
16321             o.raw = { errorMsg : response.responseText };
16322             this.fireEvent("loadexception", this, o, response, e);
16323             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16324             return;
16325         }
16326         
16327         this.fireEvent("load", this, o, o.request.arg);
16328         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16329     },
16330
16331     // private
16332     update : function(dataSet){
16333
16334     },
16335
16336     // private
16337     updateResponse : function(dataSet){
16338
16339     }
16340 });/*
16341  * Based on:
16342  * Ext JS Library 1.1.1
16343  * Copyright(c) 2006-2007, Ext JS, LLC.
16344  *
16345  * Originally Released Under LGPL - original licence link has changed is not relivant.
16346  *
16347  * Fork - LGPL
16348  * <script type="text/javascript">
16349  */
16350
16351 /**
16352  * @class Roo.data.ScriptTagProxy
16353  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16354  * other than the originating domain of the running page.<br><br>
16355  * <p>
16356  * <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
16357  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16358  * <p>
16359  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16360  * source code that is used as the source inside a &lt;script> tag.<br><br>
16361  * <p>
16362  * In order for the browser to process the returned data, the server must wrap the data object
16363  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16364  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16365  * depending on whether the callback name was passed:
16366  * <p>
16367  * <pre><code>
16368 boolean scriptTag = false;
16369 String cb = request.getParameter("callback");
16370 if (cb != null) {
16371     scriptTag = true;
16372     response.setContentType("text/javascript");
16373 } else {
16374     response.setContentType("application/x-json");
16375 }
16376 Writer out = response.getWriter();
16377 if (scriptTag) {
16378     out.write(cb + "(");
16379 }
16380 out.print(dataBlock.toJsonString());
16381 if (scriptTag) {
16382     out.write(");");
16383 }
16384 </pre></code>
16385  *
16386  * @constructor
16387  * @param {Object} config A configuration object.
16388  */
16389 Roo.data.ScriptTagProxy = function(config){
16390     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16391     Roo.apply(this, config);
16392     this.head = document.getElementsByTagName("head")[0];
16393 };
16394
16395 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16396
16397 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16398     /**
16399      * @cfg {String} url The URL from which to request the data object.
16400      */
16401     /**
16402      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16403      */
16404     timeout : 30000,
16405     /**
16406      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16407      * the server the name of the callback function set up by the load call to process the returned data object.
16408      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16409      * javascript output which calls this named function passing the data object as its only parameter.
16410      */
16411     callbackParam : "callback",
16412     /**
16413      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16414      * name to the request.
16415      */
16416     nocache : true,
16417
16418     /**
16419      * Load data from the configured URL, read the data object into
16420      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16421      * process that block using the passed callback.
16422      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16423      * for the request to the remote server.
16424      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16425      * object into a block of Roo.data.Records.
16426      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16427      * The function must be passed <ul>
16428      * <li>The Record block object</li>
16429      * <li>The "arg" argument from the load function</li>
16430      * <li>A boolean success indicator</li>
16431      * </ul>
16432      * @param {Object} scope The scope in which to call the callback
16433      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16434      */
16435     load : function(params, reader, callback, scope, arg){
16436         if(this.fireEvent("beforeload", this, params) !== false){
16437
16438             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16439
16440             var url = this.url;
16441             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16442             if(this.nocache){
16443                 url += "&_dc=" + (new Date().getTime());
16444             }
16445             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16446             var trans = {
16447                 id : transId,
16448                 cb : "stcCallback"+transId,
16449                 scriptId : "stcScript"+transId,
16450                 params : params,
16451                 arg : arg,
16452                 url : url,
16453                 callback : callback,
16454                 scope : scope,
16455                 reader : reader
16456             };
16457             var conn = this;
16458
16459             window[trans.cb] = function(o){
16460                 conn.handleResponse(o, trans);
16461             };
16462
16463             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16464
16465             if(this.autoAbort !== false){
16466                 this.abort();
16467             }
16468
16469             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16470
16471             var script = document.createElement("script");
16472             script.setAttribute("src", url);
16473             script.setAttribute("type", "text/javascript");
16474             script.setAttribute("id", trans.scriptId);
16475             this.head.appendChild(script);
16476
16477             this.trans = trans;
16478         }else{
16479             callback.call(scope||this, null, arg, false);
16480         }
16481     },
16482
16483     // private
16484     isLoading : function(){
16485         return this.trans ? true : false;
16486     },
16487
16488     /**
16489      * Abort the current server request.
16490      */
16491     abort : function(){
16492         if(this.isLoading()){
16493             this.destroyTrans(this.trans);
16494         }
16495     },
16496
16497     // private
16498     destroyTrans : function(trans, isLoaded){
16499         this.head.removeChild(document.getElementById(trans.scriptId));
16500         clearTimeout(trans.timeoutId);
16501         if(isLoaded){
16502             window[trans.cb] = undefined;
16503             try{
16504                 delete window[trans.cb];
16505             }catch(e){}
16506         }else{
16507             // if hasn't been loaded, wait for load to remove it to prevent script error
16508             window[trans.cb] = function(){
16509                 window[trans.cb] = undefined;
16510                 try{
16511                     delete window[trans.cb];
16512                 }catch(e){}
16513             };
16514         }
16515     },
16516
16517     // private
16518     handleResponse : function(o, trans){
16519         this.trans = false;
16520         this.destroyTrans(trans, true);
16521         var result;
16522         try {
16523             result = trans.reader.readRecords(o);
16524         }catch(e){
16525             this.fireEvent("loadexception", this, o, trans.arg, e);
16526             trans.callback.call(trans.scope||window, null, trans.arg, false);
16527             return;
16528         }
16529         this.fireEvent("load", this, o, trans.arg);
16530         trans.callback.call(trans.scope||window, result, trans.arg, true);
16531     },
16532
16533     // private
16534     handleFailure : function(trans){
16535         this.trans = false;
16536         this.destroyTrans(trans, false);
16537         this.fireEvent("loadexception", this, null, trans.arg);
16538         trans.callback.call(trans.scope||window, null, trans.arg, false);
16539     }
16540 });/*
16541  * Based on:
16542  * Ext JS Library 1.1.1
16543  * Copyright(c) 2006-2007, Ext JS, LLC.
16544  *
16545  * Originally Released Under LGPL - original licence link has changed is not relivant.
16546  *
16547  * Fork - LGPL
16548  * <script type="text/javascript">
16549  */
16550
16551 /**
16552  * @class Roo.data.JsonReader
16553  * @extends Roo.data.DataReader
16554  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16555  * based on mappings in a provided Roo.data.Record constructor.
16556  * 
16557  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16558  * in the reply previously. 
16559  * 
16560  * <p>
16561  * Example code:
16562  * <pre><code>
16563 var RecordDef = Roo.data.Record.create([
16564     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16565     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16566 ]);
16567 var myReader = new Roo.data.JsonReader({
16568     totalProperty: "results",    // The property which contains the total dataset size (optional)
16569     root: "rows",                // The property which contains an Array of row objects
16570     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16571 }, RecordDef);
16572 </code></pre>
16573  * <p>
16574  * This would consume a JSON file like this:
16575  * <pre><code>
16576 { 'results': 2, 'rows': [
16577     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16578     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16579 }
16580 </code></pre>
16581  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16582  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16583  * paged from the remote server.
16584  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16585  * @cfg {String} root name of the property which contains the Array of row objects.
16586  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16587  * @cfg {Array} fields Array of field definition objects
16588  * @constructor
16589  * Create a new JsonReader
16590  * @param {Object} meta Metadata configuration options
16591  * @param {Object} recordType Either an Array of field definition objects,
16592  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16593  */
16594 Roo.data.JsonReader = function(meta, recordType){
16595     
16596     meta = meta || {};
16597     // set some defaults:
16598     Roo.applyIf(meta, {
16599         totalProperty: 'total',
16600         successProperty : 'success',
16601         root : 'data',
16602         id : 'id'
16603     });
16604     
16605     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16606 };
16607 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16608     
16609     readerType : 'Json',
16610     
16611     /**
16612      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16613      * Used by Store query builder to append _requestMeta to params.
16614      * 
16615      */
16616     metaFromRemote : false,
16617     /**
16618      * This method is only used by a DataProxy which has retrieved data from a remote server.
16619      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16620      * @return {Object} data A data block which is used by an Roo.data.Store object as
16621      * a cache of Roo.data.Records.
16622      */
16623     read : function(response){
16624         var json = response.responseText;
16625        
16626         var o = /* eval:var:o */ eval("("+json+")");
16627         if(!o) {
16628             throw {message: "JsonReader.read: Json object not found"};
16629         }
16630         
16631         if(o.metaData){
16632             
16633             delete this.ef;
16634             this.metaFromRemote = true;
16635             this.meta = o.metaData;
16636             this.recordType = Roo.data.Record.create(o.metaData.fields);
16637             this.onMetaChange(this.meta, this.recordType, o);
16638         }
16639         return this.readRecords(o);
16640     },
16641
16642     // private function a store will implement
16643     onMetaChange : function(meta, recordType, o){
16644
16645     },
16646
16647     /**
16648          * @ignore
16649          */
16650     simpleAccess: function(obj, subsc) {
16651         return obj[subsc];
16652     },
16653
16654         /**
16655          * @ignore
16656          */
16657     getJsonAccessor: function(){
16658         var re = /[\[\.]/;
16659         return function(expr) {
16660             try {
16661                 return(re.test(expr))
16662                     ? new Function("obj", "return obj." + expr)
16663                     : function(obj){
16664                         return obj[expr];
16665                     };
16666             } catch(e){}
16667             return Roo.emptyFn;
16668         };
16669     }(),
16670
16671     /**
16672      * Create a data block containing Roo.data.Records from an XML document.
16673      * @param {Object} o An object which contains an Array of row objects in the property specified
16674      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16675      * which contains the total size of the dataset.
16676      * @return {Object} data A data block which is used by an Roo.data.Store object as
16677      * a cache of Roo.data.Records.
16678      */
16679     readRecords : function(o){
16680         /**
16681          * After any data loads, the raw JSON data is available for further custom processing.
16682          * @type Object
16683          */
16684         this.o = o;
16685         var s = this.meta, Record = this.recordType,
16686             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16687
16688 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16689         if (!this.ef) {
16690             if(s.totalProperty) {
16691                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16692                 }
16693                 if(s.successProperty) {
16694                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16695                 }
16696                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16697                 if (s.id) {
16698                         var g = this.getJsonAccessor(s.id);
16699                         this.getId = function(rec) {
16700                                 var r = g(rec);  
16701                                 return (r === undefined || r === "") ? null : r;
16702                         };
16703                 } else {
16704                         this.getId = function(){return null;};
16705                 }
16706             this.ef = [];
16707             for(var jj = 0; jj < fl; jj++){
16708                 f = fi[jj];
16709                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16710                 this.ef[jj] = this.getJsonAccessor(map);
16711             }
16712         }
16713
16714         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16715         if(s.totalProperty){
16716             var vt = parseInt(this.getTotal(o), 10);
16717             if(!isNaN(vt)){
16718                 totalRecords = vt;
16719             }
16720         }
16721         if(s.successProperty){
16722             var vs = this.getSuccess(o);
16723             if(vs === false || vs === 'false'){
16724                 success = false;
16725             }
16726         }
16727         var records = [];
16728         for(var i = 0; i < c; i++){
16729             var n = root[i];
16730             var values = {};
16731             var id = this.getId(n);
16732             for(var j = 0; j < fl; j++){
16733                 f = fi[j];
16734                                 var v = this.ef[j](n);
16735                                 if (!f.convert) {
16736                                         Roo.log('missing convert for ' + f.name);
16737                                         Roo.log(f);
16738                                         continue;
16739                                 }
16740                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16741             }
16742                         if (!Record) {
16743                                 return {
16744                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16745                                         success : false,
16746                                         records : [],
16747                                         totalRecords : 0
16748                                 };
16749                         }
16750             var record = new Record(values, id);
16751             record.json = n;
16752             records[i] = record;
16753         }
16754         return {
16755             raw : o,
16756             success : success,
16757             records : records,
16758             totalRecords : totalRecords
16759         };
16760     },
16761     // used when loading children.. @see loadDataFromChildren
16762     toLoadData: function(rec)
16763     {
16764         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16765         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16766         return { data : data, total : data.length };
16767         
16768     }
16769 });/*
16770  * Based on:
16771  * Ext JS Library 1.1.1
16772  * Copyright(c) 2006-2007, Ext JS, LLC.
16773  *
16774  * Originally Released Under LGPL - original licence link has changed is not relivant.
16775  *
16776  * Fork - LGPL
16777  * <script type="text/javascript">
16778  */
16779
16780 /**
16781  * @class Roo.data.ArrayReader
16782  * @extends Roo.data.DataReader
16783  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16784  * Each element of that Array represents a row of data fields. The
16785  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16786  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16787  * <p>
16788  * Example code:.
16789  * <pre><code>
16790 var RecordDef = Roo.data.Record.create([
16791     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16792     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16793 ]);
16794 var myReader = new Roo.data.ArrayReader({
16795     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16796 }, RecordDef);
16797 </code></pre>
16798  * <p>
16799  * This would consume an Array like this:
16800  * <pre><code>
16801 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16802   </code></pre>
16803  
16804  * @constructor
16805  * Create a new JsonReader
16806  * @param {Object} meta Metadata configuration options.
16807  * @param {Object|Array} recordType Either an Array of field definition objects
16808  * 
16809  * @cfg {Array} fields Array of field definition objects
16810  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16811  * as specified to {@link Roo.data.Record#create},
16812  * or an {@link Roo.data.Record} object
16813  *
16814  * 
16815  * created using {@link Roo.data.Record#create}.
16816  */
16817 Roo.data.ArrayReader = function(meta, recordType)
16818 {    
16819     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16820 };
16821
16822 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16823     
16824       /**
16825      * Create a data block containing Roo.data.Records from an XML document.
16826      * @param {Object} o An Array of row objects which represents the dataset.
16827      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16828      * a cache of Roo.data.Records.
16829      */
16830     readRecords : function(o)
16831     {
16832         var sid = this.meta ? this.meta.id : null;
16833         var recordType = this.recordType, fields = recordType.prototype.fields;
16834         var records = [];
16835         var root = o;
16836         for(var i = 0; i < root.length; i++){
16837             var n = root[i];
16838             var values = {};
16839             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16840             for(var j = 0, jlen = fields.length; j < jlen; j++){
16841                 var f = fields.items[j];
16842                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16843                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16844                 v = f.convert(v);
16845                 values[f.name] = v;
16846             }
16847             var record = new recordType(values, id);
16848             record.json = n;
16849             records[records.length] = record;
16850         }
16851         return {
16852             records : records,
16853             totalRecords : records.length
16854         };
16855     },
16856     // used when loading children.. @see loadDataFromChildren
16857     toLoadData: function(rec)
16858     {
16859         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16860         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16861         
16862     }
16863     
16864     
16865 });/*
16866  * - LGPL
16867  * * 
16868  */
16869
16870 /**
16871  * @class Roo.bootstrap.form.ComboBox
16872  * @extends Roo.bootstrap.form.TriggerField
16873  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16874  * @cfg {Boolean} append (true|false) default false
16875  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16876  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16877  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16878  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16879  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16880  * @cfg {Boolean} animate default true
16881  * @cfg {Boolean} emptyResultText only for touch device
16882  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16883  * @cfg {String} emptyTitle default ''
16884  * @cfg {Number} width fixed with? experimental
16885  * @constructor
16886  * Create a new ComboBox.
16887  * @param {Object} config Configuration options
16888  */
16889 Roo.bootstrap.form.ComboBox = function(config){
16890     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16891     this.addEvents({
16892         /**
16893          * @event expand
16894          * Fires when the dropdown list is expanded
16895         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16896         */
16897         'expand' : true,
16898         /**
16899          * @event collapse
16900          * Fires when the dropdown list is collapsed
16901         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16902         */
16903         'collapse' : true,
16904         /**
16905          * @event beforeselect
16906          * Fires before a list item is selected. Return false to cancel the selection.
16907         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16908         * @param {Roo.data.Record} record The data record returned from the underlying store
16909         * @param {Number} index The index of the selected item in the dropdown list
16910         */
16911         'beforeselect' : true,
16912         /**
16913          * @event select
16914          * Fires when a list item is selected
16915         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16916         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16917         * @param {Number} index The index of the selected item in the dropdown list
16918         */
16919         'select' : true,
16920         /**
16921          * @event beforequery
16922          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16923          * The event object passed has these properties:
16924         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16925         * @param {String} query The query
16926         * @param {Boolean} forceAll true to force "all" query
16927         * @param {Boolean} cancel true to cancel the query
16928         * @param {Object} e The query event object
16929         */
16930         'beforequery': true,
16931          /**
16932          * @event add
16933          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16934         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16935         */
16936         'add' : true,
16937         /**
16938          * @event edit
16939          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16940         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16941         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16942         */
16943         'edit' : true,
16944         /**
16945          * @event remove
16946          * Fires when the remove value from the combobox array
16947         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16948         */
16949         'remove' : true,
16950         /**
16951          * @event afterremove
16952          * Fires when the remove value from the combobox array
16953         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16954         */
16955         'afterremove' : true,
16956         /**
16957          * @event specialfilter
16958          * Fires when specialfilter
16959             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16960             */
16961         'specialfilter' : true,
16962         /**
16963          * @event tick
16964          * Fires when tick the element
16965             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16966             */
16967         'tick' : true,
16968         /**
16969          * @event touchviewdisplay
16970          * Fires when touch view require special display (default is using displayField)
16971             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16972             * @param {Object} cfg set html .
16973             */
16974         'touchviewdisplay' : true
16975         
16976     });
16977     
16978     this.item = [];
16979     this.tickItems = [];
16980     
16981     this.selectedIndex = -1;
16982     if(this.mode == 'local'){
16983         if(config.queryDelay === undefined){
16984             this.queryDelay = 10;
16985         }
16986         if(config.minChars === undefined){
16987             this.minChars = 0;
16988         }
16989     }
16990 };
16991
16992 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16993      
16994     /**
16995      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16996      * rendering into an Roo.Editor, defaults to false)
16997      */
16998     /**
16999      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
17000      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
17001      */
17002     /**
17003      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
17004      */
17005     /**
17006      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
17007      * the dropdown list (defaults to undefined, with no header element)
17008      */
17009
17010      /**
17011      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
17012      */
17013      
17014      /**
17015      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
17016      */
17017     listWidth: undefined,
17018     /**
17019      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
17020      * mode = 'remote' or 'text' if mode = 'local')
17021      */
17022     displayField: undefined,
17023     
17024     /**
17025      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
17026      * mode = 'remote' or 'value' if mode = 'local'). 
17027      * Note: use of a valueField requires the user make a selection
17028      * in order for a value to be mapped.
17029      */
17030     valueField: undefined,
17031     /**
17032      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
17033      */
17034     modalTitle : '',
17035     
17036     /**
17037      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
17038      * field's data value (defaults to the underlying DOM element's name)
17039      */
17040     hiddenName: undefined,
17041     /**
17042      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17043      */
17044     listClass: '',
17045     /**
17046      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17047      */
17048     selectedClass: 'active',
17049     
17050     /**
17051      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17052      */
17053     shadow:'sides',
17054     /**
17055      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17056      * anchor positions (defaults to 'tl-bl')
17057      */
17058     listAlign: 'tl-bl?',
17059     /**
17060      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17061      */
17062     maxHeight: 300,
17063     /**
17064      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17065      * query specified by the allQuery config option (defaults to 'query')
17066      */
17067     triggerAction: 'query',
17068     /**
17069      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17070      * (defaults to 4, does not apply if editable = false)
17071      */
17072     minChars : 4,
17073     /**
17074      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17075      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17076      */
17077     typeAhead: false,
17078     /**
17079      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17080      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17081      */
17082     queryDelay: 500,
17083     /**
17084      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17085      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17086      */
17087     pageSize: 0,
17088     /**
17089      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17090      * when editable = true (defaults to false)
17091      */
17092     selectOnFocus:false,
17093     /**
17094      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17095      */
17096     queryParam: 'query',
17097     /**
17098      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17099      * when mode = 'remote' (defaults to 'Loading...')
17100      */
17101     loadingText: 'Loading...',
17102     /**
17103      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17104      */
17105     resizable: false,
17106     /**
17107      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17108      */
17109     handleHeight : 8,
17110     /**
17111      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17112      * traditional select (defaults to true)
17113      */
17114     editable: true,
17115     /**
17116      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17117      */
17118     allQuery: '',
17119     /**
17120      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17121      */
17122     mode: 'remote',
17123     /**
17124      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17125      * listWidth has a higher value)
17126      */
17127     minListWidth : 70,
17128     /**
17129      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17130      * allow the user to set arbitrary text into the field (defaults to false)
17131      */
17132     forceSelection:false,
17133     /**
17134      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17135      * if typeAhead = true (defaults to 250)
17136      */
17137     typeAheadDelay : 250,
17138     /**
17139      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17140      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17141      */
17142     valueNotFoundText : undefined,
17143     /**
17144      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17145      */
17146     blockFocus : false,
17147     
17148     /**
17149      * @cfg {Boolean} disableClear Disable showing of clear button.
17150      */
17151     disableClear : false,
17152     /**
17153      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17154      */
17155     alwaysQuery : false,
17156     
17157     /**
17158      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17159      */
17160     multiple : false,
17161     
17162     /**
17163      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17164      */
17165     invalidClass : "has-warning",
17166     
17167     /**
17168      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17169      */
17170     validClass : "has-success",
17171     
17172     /**
17173      * @cfg {Boolean} specialFilter (true|false) special filter default false
17174      */
17175     specialFilter : false,
17176     
17177     /**
17178      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17179      */
17180     mobileTouchView : true,
17181     
17182     /**
17183      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17184      */
17185     useNativeIOS : false,
17186     
17187     /**
17188      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17189      */
17190     mobile_restrict_height : false,
17191     
17192     ios_options : false,
17193     
17194     //private
17195     addicon : false,
17196     editicon: false,
17197     
17198     page: 0,
17199     hasQuery: false,
17200     append: false,
17201     loadNext: false,
17202     autoFocus : true,
17203     tickable : false,
17204     btnPosition : 'right',
17205     triggerList : true,
17206     showToggleBtn : true,
17207     animate : true,
17208     emptyResultText: 'Empty',
17209     triggerText : 'Select',
17210     emptyTitle : '',
17211     width : false,
17212     
17213     // element that contains real text value.. (when hidden is used..)
17214     
17215     getAutoCreate : function()
17216     {   
17217         var cfg = false;
17218         //render
17219         /*
17220          * Render classic select for iso
17221          */
17222         
17223         if(Roo.isIOS && this.useNativeIOS){
17224             cfg = this.getAutoCreateNativeIOS();
17225             return cfg;
17226         }
17227         
17228         /*
17229          * Touch Devices
17230          */
17231         
17232         if(Roo.isTouch && this.mobileTouchView){
17233             cfg = this.getAutoCreateTouchView();
17234             return cfg;;
17235         }
17236         
17237         /*
17238          *  Normal ComboBox
17239          */
17240         if(!this.tickable){
17241             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17242             return cfg;
17243         }
17244         
17245         /*
17246          *  ComboBox with tickable selections
17247          */
17248              
17249         var align = this.labelAlign || this.parentLabelAlign();
17250         
17251         cfg = {
17252             cls : 'form-group roo-combobox-tickable' //input-group
17253         };
17254         
17255         var btn_text_select = '';
17256         var btn_text_done = '';
17257         var btn_text_cancel = '';
17258         
17259         if (this.btn_text_show) {
17260             btn_text_select = 'Select';
17261             btn_text_done = 'Done';
17262             btn_text_cancel = 'Cancel'; 
17263         }
17264         
17265         var buttons = {
17266             tag : 'div',
17267             cls : 'tickable-buttons',
17268             cn : [
17269                 {
17270                     tag : 'button',
17271                     type : 'button',
17272                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17273                     //html : this.triggerText
17274                     html: btn_text_select
17275                 },
17276                 {
17277                     tag : 'button',
17278                     type : 'button',
17279                     name : 'ok',
17280                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17281                     //html : 'Done'
17282                     html: btn_text_done
17283                 },
17284                 {
17285                     tag : 'button',
17286                     type : 'button',
17287                     name : 'cancel',
17288                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17289                     //html : 'Cancel'
17290                     html: btn_text_cancel
17291                 }
17292             ]
17293         };
17294         
17295         if(this.editable){
17296             buttons.cn.unshift({
17297                 tag: 'input',
17298                 cls: 'roo-select2-search-field-input'
17299             });
17300         }
17301         
17302         var _this = this;
17303         
17304         Roo.each(buttons.cn, function(c){
17305             if (_this.size) {
17306                 c.cls += ' btn-' + _this.size;
17307             }
17308
17309             if (_this.disabled) {
17310                 c.disabled = true;
17311             }
17312         });
17313         
17314         var box = {
17315             tag: 'div',
17316             style : 'display: contents',
17317             cn: [
17318                 {
17319                     tag: 'input',
17320                     type : 'hidden',
17321                     cls: 'form-hidden-field'
17322                 },
17323                 {
17324                     tag: 'ul',
17325                     cls: 'roo-select2-choices',
17326                     cn:[
17327                         {
17328                             tag: 'li',
17329                             cls: 'roo-select2-search-field',
17330                             cn: [
17331                                 buttons
17332                             ]
17333                         }
17334                     ]
17335                 }
17336             ]
17337         };
17338         
17339         var combobox = {
17340             cls: 'roo-select2-container input-group roo-select2-container-multi',
17341             cn: [
17342                 
17343                 box
17344 //                {
17345 //                    tag: 'ul',
17346 //                    cls: 'typeahead typeahead-long dropdown-menu',
17347 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17348 //                }
17349             ]
17350         };
17351         
17352         if(this.hasFeedback && !this.allowBlank){
17353             
17354             var feedback = {
17355                 tag: 'span',
17356                 cls: 'glyphicon form-control-feedback'
17357             };
17358
17359             combobox.cn.push(feedback);
17360         }
17361         
17362         
17363         
17364         var indicator = {
17365             tag : 'i',
17366             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17367             tooltip : 'This field is required'
17368         };
17369         if (Roo.bootstrap.version == 4) {
17370             indicator = {
17371                 tag : 'i',
17372                 style : 'display:none'
17373             };
17374         }
17375         if (align ==='left' && this.fieldLabel.length) {
17376             
17377             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17378             
17379             cfg.cn = [
17380                 indicator,
17381                 {
17382                     tag: 'label',
17383                     'for' :  id,
17384                     cls : 'control-label col-form-label',
17385                     html : this.fieldLabel
17386
17387                 },
17388                 {
17389                     cls : "", 
17390                     cn: [
17391                         combobox
17392                     ]
17393                 }
17394
17395             ];
17396             
17397             var labelCfg = cfg.cn[1];
17398             var contentCfg = cfg.cn[2];
17399             
17400
17401             if(this.indicatorpos == 'right'){
17402                 
17403                 cfg.cn = [
17404                     {
17405                         tag: 'label',
17406                         'for' :  id,
17407                         cls : 'control-label col-form-label',
17408                         cn : [
17409                             {
17410                                 tag : 'span',
17411                                 html : this.fieldLabel
17412                             },
17413                             indicator
17414                         ]
17415                     },
17416                     {
17417                         cls : "",
17418                         cn: [
17419                             combobox
17420                         ]
17421                     }
17422
17423                 ];
17424                 
17425                 
17426                 
17427                 labelCfg = cfg.cn[0];
17428                 contentCfg = cfg.cn[1];
17429             
17430             }
17431             
17432             if(this.labelWidth > 12){
17433                 labelCfg.style = "width: " + this.labelWidth + 'px';
17434             }
17435             if(this.width * 1 > 0){
17436                 contentCfg.style = "width: " + this.width + 'px';
17437             }
17438             if(this.labelWidth < 13 && this.labelmd == 0){
17439                 this.labelmd = this.labelWidth;
17440             }
17441             
17442             if(this.labellg > 0){
17443                 labelCfg.cls += ' col-lg-' + this.labellg;
17444                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17445             }
17446             
17447             if(this.labelmd > 0){
17448                 labelCfg.cls += ' col-md-' + this.labelmd;
17449                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17450             }
17451             
17452             if(this.labelsm > 0){
17453                 labelCfg.cls += ' col-sm-' + this.labelsm;
17454                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17455             }
17456             
17457             if(this.labelxs > 0){
17458                 labelCfg.cls += ' col-xs-' + this.labelxs;
17459                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17460             }
17461                 
17462                 
17463         } else if ( this.fieldLabel.length) {
17464 //                Roo.log(" label");
17465                  cfg.cn = [
17466                    indicator,
17467                     {
17468                         tag: 'label',
17469                         //cls : 'input-group-addon',
17470                         html : this.fieldLabel
17471                     },
17472                     combobox
17473                 ];
17474                 
17475                 if(this.indicatorpos == 'right'){
17476                     cfg.cn = [
17477                         {
17478                             tag: 'label',
17479                             //cls : 'input-group-addon',
17480                             html : this.fieldLabel
17481                         },
17482                         indicator,
17483                         combobox
17484                     ];
17485                     
17486                 }
17487
17488         } else {
17489             
17490 //                Roo.log(" no label && no align");
17491                 cfg = combobox
17492                      
17493                 
17494         }
17495          
17496         var settings=this;
17497         ['xs','sm','md','lg'].map(function(size){
17498             if (settings[size]) {
17499                 cfg.cls += ' col-' + size + '-' + settings[size];
17500             }
17501         });
17502         
17503         return cfg;
17504         
17505     },
17506     
17507     _initEventsCalled : false,
17508     
17509     // private
17510     initEvents: function()
17511     {   
17512         if (this._initEventsCalled) { // as we call render... prevent looping...
17513             return;
17514         }
17515         this._initEventsCalled = true;
17516         
17517         if (!this.store) {
17518             throw "can not find store for combo";
17519         }
17520         
17521         this.indicator = this.indicatorEl();
17522         
17523         this.store = Roo.factory(this.store, Roo.data);
17524         this.store.parent = this;
17525         
17526         // if we are building from html. then this element is so complex, that we can not really
17527         // use the rendered HTML.
17528         // so we have to trash and replace the previous code.
17529         if (Roo.XComponent.build_from_html) {
17530             // remove this element....
17531             var e = this.el.dom, k=0;
17532             while (e ) { e = e.previousSibling;  ++k;}
17533
17534             this.el.remove();
17535             
17536             this.el=false;
17537             this.rendered = false;
17538             
17539             this.render(this.parent().getChildContainer(true), k);
17540         }
17541         
17542         if(Roo.isIOS && this.useNativeIOS){
17543             this.initIOSView();
17544             return;
17545         }
17546         
17547         /*
17548          * Touch Devices
17549          */
17550         
17551         if(Roo.isTouch && this.mobileTouchView){
17552             this.initTouchView();
17553             return;
17554         }
17555         
17556         if(this.tickable){
17557             this.initTickableEvents();
17558             return;
17559         }
17560         
17561         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17562         
17563         if(this.hiddenName){
17564             
17565             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17566             
17567             this.hiddenField.dom.value =
17568                 this.hiddenValue !== undefined ? this.hiddenValue :
17569                 this.value !== undefined ? this.value : '';
17570
17571             // prevent input submission
17572             this.el.dom.removeAttribute('name');
17573             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17574              
17575              
17576         }
17577         //if(Roo.isGecko){
17578         //    this.el.dom.setAttribute('autocomplete', 'off');
17579         //}
17580         
17581         var cls = 'x-combo-list';
17582         
17583         //this.list = new Roo.Layer({
17584         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17585         //});
17586         
17587         var _this = this;
17588         
17589         (function(){
17590             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17591             _this.list.setWidth(lw);
17592         }).defer(100);
17593         
17594         this.list.on('mouseover', this.onViewOver, this);
17595         this.list.on('mousemove', this.onViewMove, this);
17596         this.list.on('scroll', this.onViewScroll, this);
17597         
17598         /*
17599         this.list.swallowEvent('mousewheel');
17600         this.assetHeight = 0;
17601
17602         if(this.title){
17603             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17604             this.assetHeight += this.header.getHeight();
17605         }
17606
17607         this.innerList = this.list.createChild({cls:cls+'-inner'});
17608         this.innerList.on('mouseover', this.onViewOver, this);
17609         this.innerList.on('mousemove', this.onViewMove, this);
17610         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17611         
17612         if(this.allowBlank && !this.pageSize && !this.disableClear){
17613             this.footer = this.list.createChild({cls:cls+'-ft'});
17614             this.pageTb = new Roo.Toolbar(this.footer);
17615            
17616         }
17617         if(this.pageSize){
17618             this.footer = this.list.createChild({cls:cls+'-ft'});
17619             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17620                     {pageSize: this.pageSize});
17621             
17622         }
17623         
17624         if (this.pageTb && this.allowBlank && !this.disableClear) {
17625             var _this = this;
17626             this.pageTb.add(new Roo.Toolbar.Fill(), {
17627                 cls: 'x-btn-icon x-btn-clear',
17628                 text: '&#160;',
17629                 handler: function()
17630                 {
17631                     _this.collapse();
17632                     _this.clearValue();
17633                     _this.onSelect(false, -1);
17634                 }
17635             });
17636         }
17637         if (this.footer) {
17638             this.assetHeight += this.footer.getHeight();
17639         }
17640         */
17641             
17642         if(!this.tpl){
17643             this.tpl = Roo.bootstrap.version == 4 ?
17644                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17645                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17646         }
17647
17648         this.view = new Roo.View(this.list, this.tpl, {
17649             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17650         });
17651         //this.view.wrapEl.setDisplayed(false);
17652         this.view.on('click', this.onViewClick, this);
17653         
17654         
17655         this.store.on('beforeload', this.onBeforeLoad, this);
17656         this.store.on('load', this.onLoad, this);
17657         this.store.on('loadexception', this.onLoadException, this);
17658         /*
17659         if(this.resizable){
17660             this.resizer = new Roo.Resizable(this.list,  {
17661                pinned:true, handles:'se'
17662             });
17663             this.resizer.on('resize', function(r, w, h){
17664                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17665                 this.listWidth = w;
17666                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17667                 this.restrictHeight();
17668             }, this);
17669             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17670         }
17671         */
17672         if(!this.editable){
17673             this.editable = true;
17674             this.setEditable(false);
17675         }
17676         
17677         /*
17678         
17679         if (typeof(this.events.add.listeners) != 'undefined') {
17680             
17681             this.addicon = this.wrap.createChild(
17682                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17683        
17684             this.addicon.on('click', function(e) {
17685                 this.fireEvent('add', this);
17686             }, this);
17687         }
17688         if (typeof(this.events.edit.listeners) != 'undefined') {
17689             
17690             this.editicon = this.wrap.createChild(
17691                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17692             if (this.addicon) {
17693                 this.editicon.setStyle('margin-left', '40px');
17694             }
17695             this.editicon.on('click', function(e) {
17696                 
17697                 // we fire even  if inothing is selected..
17698                 this.fireEvent('edit', this, this.lastData );
17699                 
17700             }, this);
17701         }
17702         */
17703         
17704         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17705             "up" : function(e){
17706                 this.inKeyMode = true;
17707                 this.selectPrev();
17708             },
17709
17710             "down" : function(e){
17711                 if(!this.isExpanded()){
17712                     this.onTriggerClick();
17713                 }else{
17714                     this.inKeyMode = true;
17715                     this.selectNext();
17716                 }
17717             },
17718
17719             "enter" : function(e){
17720 //                this.onViewClick();
17721                 //return true;
17722                 this.collapse();
17723                 
17724                 if(this.fireEvent("specialkey", this, e)){
17725                     this.onViewClick(false);
17726                 }
17727                 
17728                 return true;
17729             },
17730
17731             "esc" : function(e){
17732                 this.collapse();
17733             },
17734
17735             "tab" : function(e){
17736                 this.collapse();
17737                 
17738                 if(this.fireEvent("specialkey", this, e)){
17739                     this.onViewClick(false);
17740                 }
17741                 
17742                 return true;
17743             },
17744
17745             scope : this,
17746
17747             doRelay : function(foo, bar, hname){
17748                 if(hname == 'down' || this.scope.isExpanded()){
17749                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17750                 }
17751                 return true;
17752             },
17753
17754             forceKeyDown: true
17755         });
17756         
17757         
17758         this.queryDelay = Math.max(this.queryDelay || 10,
17759                 this.mode == 'local' ? 10 : 250);
17760         
17761         
17762         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17763         
17764         if(this.typeAhead){
17765             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17766         }
17767         if(this.editable !== false){
17768             this.inputEl().on("keyup", this.onKeyUp, this);
17769         }
17770         if(this.forceSelection){
17771             this.inputEl().on('blur', this.doForce, this);
17772         }
17773         
17774         if(this.multiple){
17775             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17776             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17777         }
17778     },
17779     
17780     initTickableEvents: function()
17781     {   
17782         this.createList();
17783         
17784         if(this.hiddenName){
17785             
17786             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17787             
17788             this.hiddenField.dom.value =
17789                 this.hiddenValue !== undefined ? this.hiddenValue :
17790                 this.value !== undefined ? this.value : '';
17791
17792             // prevent input submission
17793             this.el.dom.removeAttribute('name');
17794             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17795              
17796              
17797         }
17798         
17799 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17800         
17801         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17802         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17803         if(this.triggerList){
17804             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17805         }
17806          
17807         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17808         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17809         
17810         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17811         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17812         
17813         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17814         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17815         
17816         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17817         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17818         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17819         
17820         this.okBtn.hide();
17821         this.cancelBtn.hide();
17822         
17823         var _this = this;
17824         
17825         (function(){
17826             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17827             _this.list.setWidth(lw);
17828         }).defer(100);
17829         
17830         this.list.on('mouseover', this.onViewOver, this);
17831         this.list.on('mousemove', this.onViewMove, this);
17832         
17833         this.list.on('scroll', this.onViewScroll, this);
17834         
17835         if(!this.tpl){
17836             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17837                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17838         }
17839
17840         this.view = new Roo.View(this.list, this.tpl, {
17841             singleSelect:true,
17842             tickable:true,
17843             parent:this,
17844             store: this.store,
17845             selectedClass: this.selectedClass
17846         });
17847         
17848         //this.view.wrapEl.setDisplayed(false);
17849         this.view.on('click', this.onViewClick, this);
17850         
17851         
17852         
17853         this.store.on('beforeload', this.onBeforeLoad, this);
17854         this.store.on('load', this.onLoad, this);
17855         this.store.on('loadexception', this.onLoadException, this);
17856         
17857         if(this.editable){
17858             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17859                 "up" : function(e){
17860                     this.inKeyMode = true;
17861                     this.selectPrev();
17862                 },
17863
17864                 "down" : function(e){
17865                     this.inKeyMode = true;
17866                     this.selectNext();
17867                 },
17868
17869                 "enter" : function(e){
17870                     if(this.fireEvent("specialkey", this, e)){
17871                         this.onViewClick(false);
17872                     }
17873                     
17874                     return true;
17875                 },
17876
17877                 "esc" : function(e){
17878                     this.onTickableFooterButtonClick(e, false, false);
17879                 },
17880
17881                 "tab" : function(e){
17882                     this.fireEvent("specialkey", this, e);
17883                     
17884                     this.onTickableFooterButtonClick(e, false, false);
17885                     
17886                     return true;
17887                 },
17888
17889                 scope : this,
17890
17891                 doRelay : function(e, fn, key){
17892                     if(this.scope.isExpanded()){
17893                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17894                     }
17895                     return true;
17896                 },
17897
17898                 forceKeyDown: true
17899             });
17900         }
17901         
17902         this.queryDelay = Math.max(this.queryDelay || 10,
17903                 this.mode == 'local' ? 10 : 250);
17904         
17905         
17906         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17907         
17908         if(this.typeAhead){
17909             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17910         }
17911         
17912         if(this.editable !== false){
17913             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17914         }
17915         
17916         this.indicator = this.indicatorEl();
17917         
17918         if(this.indicator){
17919             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17920             this.indicator.hide();
17921         }
17922         
17923     },
17924
17925     onDestroy : function(){
17926         if(this.view){
17927             this.view.setStore(null);
17928             this.view.el.removeAllListeners();
17929             this.view.el.remove();
17930             this.view.purgeListeners();
17931         }
17932         if(this.list){
17933             this.list.dom.innerHTML  = '';
17934         }
17935         
17936         if(this.store){
17937             this.store.un('beforeload', this.onBeforeLoad, this);
17938             this.store.un('load', this.onLoad, this);
17939             this.store.un('loadexception', this.onLoadException, this);
17940         }
17941         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17942     },
17943
17944     // private
17945     fireKey : function(e){
17946         if(e.isNavKeyPress() && !this.list.isVisible()){
17947             this.fireEvent("specialkey", this, e);
17948         }
17949     },
17950
17951     // private
17952     onResize: function(w, h)
17953     {
17954         
17955         
17956 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17957 //        
17958 //        if(typeof w != 'number'){
17959 //            // we do not handle it!?!?
17960 //            return;
17961 //        }
17962 //        var tw = this.trigger.getWidth();
17963 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17964 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17965 //        var x = w - tw;
17966 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17967 //            
17968 //        //this.trigger.setStyle('left', x+'px');
17969 //        
17970 //        if(this.list && this.listWidth === undefined){
17971 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17972 //            this.list.setWidth(lw);
17973 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17974 //        }
17975         
17976     
17977         
17978     },
17979
17980     /**
17981      * Allow or prevent the user from directly editing the field text.  If false is passed,
17982      * the user will only be able to select from the items defined in the dropdown list.  This method
17983      * is the runtime equivalent of setting the 'editable' config option at config time.
17984      * @param {Boolean} value True to allow the user to directly edit the field text
17985      */
17986     setEditable : function(value){
17987         if(value == this.editable){
17988             return;
17989         }
17990         this.editable = value;
17991         if(!value){
17992             this.inputEl().dom.setAttribute('readOnly', true);
17993             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17994             this.inputEl().addClass('x-combo-noedit');
17995         }else{
17996             this.inputEl().dom.removeAttribute('readOnly');
17997             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17998             this.inputEl().removeClass('x-combo-noedit');
17999         }
18000     },
18001
18002     // private
18003     
18004     onBeforeLoad : function(combo,opts){
18005         if(!this.hasFocus){
18006             return;
18007         }
18008          if (!opts.add) {
18009             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
18010          }
18011         this.restrictHeight();
18012         this.selectedIndex = -1;
18013     },
18014
18015     // private
18016     onLoad : function(){
18017         
18018         this.hasQuery = false;
18019         
18020         if(!this.hasFocus){
18021             return;
18022         }
18023         
18024         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18025             this.loading.hide();
18026         }
18027         
18028         if(this.store.getCount() > 0){
18029             
18030             this.expand();
18031             this.restrictHeight();
18032             if(this.lastQuery == this.allQuery){
18033                 if(this.editable && !this.tickable){
18034                     this.inputEl().dom.select();
18035                 }
18036                 
18037                 if(
18038                     !this.selectByValue(this.value, true) &&
18039                     this.autoFocus && 
18040                     (
18041                         !this.store.lastOptions ||
18042                         typeof(this.store.lastOptions.add) == 'undefined' || 
18043                         this.store.lastOptions.add != true
18044                     )
18045                 ){
18046                     this.select(0, true);
18047                 }
18048             }else{
18049                 if(this.autoFocus){
18050                     this.selectNext();
18051                 }
18052                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18053                     this.taTask.delay(this.typeAheadDelay);
18054                 }
18055             }
18056         }else{
18057             this.onEmptyResults();
18058         }
18059         
18060         //this.el.focus();
18061     },
18062     // private
18063     onLoadException : function()
18064     {
18065         this.hasQuery = false;
18066         
18067         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18068             this.loading.hide();
18069         }
18070         
18071         if(this.tickable && this.editable){
18072             return;
18073         }
18074         
18075         this.collapse();
18076         // only causes errors at present
18077         //Roo.log(this.store.reader.jsonData);
18078         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18079             // fixme
18080             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18081         //}
18082         
18083         
18084     },
18085     // private
18086     onTypeAhead : function(){
18087         if(this.store.getCount() > 0){
18088             var r = this.store.getAt(0);
18089             var newValue = r.data[this.displayField];
18090             var len = newValue.length;
18091             var selStart = this.getRawValue().length;
18092             
18093             if(selStart != len){
18094                 this.setRawValue(newValue);
18095                 this.selectText(selStart, newValue.length);
18096             }
18097         }
18098     },
18099
18100     // private
18101     onSelect : function(record, index){
18102         
18103         if(this.fireEvent('beforeselect', this, record, index) !== false){
18104         
18105             this.setFromData(index > -1 ? record.data : false);
18106             
18107             this.collapse();
18108             this.fireEvent('select', this, record, index);
18109         }
18110     },
18111
18112     /**
18113      * Returns the currently selected field value or empty string if no value is set.
18114      * @return {String} value The selected value
18115      */
18116     getValue : function()
18117     {
18118         if(Roo.isIOS && this.useNativeIOS){
18119             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18120         }
18121         
18122         if(this.multiple){
18123             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18124         }
18125         
18126         if(this.valueField){
18127             return typeof this.value != 'undefined' ? this.value : '';
18128         }else{
18129             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18130         }
18131     },
18132     
18133     getRawValue : function()
18134     {
18135         if(Roo.isIOS && this.useNativeIOS){
18136             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18137         }
18138         
18139         var v = this.inputEl().getValue();
18140         
18141         return v;
18142     },
18143
18144     /**
18145      * Clears any text/value currently set in the field
18146      */
18147     clearValue : function(){
18148         
18149         if(this.hiddenField){
18150             this.hiddenField.dom.value = '';
18151         }
18152         this.value = '';
18153         this.setRawValue('');
18154         this.lastSelectionText = '';
18155         this.lastData = false;
18156         
18157         var close = this.closeTriggerEl();
18158         
18159         if(close){
18160             close.hide();
18161         }
18162         
18163         this.validate();
18164         
18165     },
18166
18167     /**
18168      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18169      * will be displayed in the field.  If the value does not match the data value of an existing item,
18170      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18171      * Otherwise the field will be blank (although the value will still be set).
18172      * @param {String} value The value to match
18173      */
18174     setValue : function(v)
18175     {
18176         if(Roo.isIOS && this.useNativeIOS){
18177             this.setIOSValue(v);
18178             return;
18179         }
18180         
18181         if(this.multiple){
18182             this.syncValue();
18183             return;
18184         }
18185         
18186         var text = v;
18187         if(this.valueField){
18188             var r = this.findRecord(this.valueField, v);
18189             if(r){
18190                 text = r.data[this.displayField];
18191             }else if(this.valueNotFoundText !== undefined){
18192                 text = this.valueNotFoundText;
18193             }
18194         }
18195         this.lastSelectionText = text;
18196         if(this.hiddenField){
18197             this.hiddenField.dom.value = v;
18198         }
18199         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18200         this.value = v;
18201         
18202         var close = this.closeTriggerEl();
18203         
18204         if(close){
18205             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18206         }
18207         
18208         this.validate();
18209     },
18210     /**
18211      * @property {Object} the last set data for the element
18212      */
18213     
18214     lastData : false,
18215     /**
18216      * Sets the value of the field based on a object which is related to the record format for the store.
18217      * @param {Object} value the value to set as. or false on reset?
18218      */
18219     setFromData : function(o){
18220         
18221         if(this.multiple){
18222             this.addItem(o);
18223             return;
18224         }
18225             
18226         var dv = ''; // display value
18227         var vv = ''; // value value..
18228         this.lastData = o;
18229         if (this.displayField) {
18230             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18231         } else {
18232             // this is an error condition!!!
18233             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18234         }
18235         
18236         if(this.valueField){
18237             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18238         }
18239         
18240         var close = this.closeTriggerEl();
18241         
18242         if(close){
18243             if(dv.length || vv * 1 > 0){
18244                 close.show() ;
18245                 this.blockFocus=true;
18246             } else {
18247                 close.hide();
18248             }             
18249         }
18250         
18251         if(this.hiddenField){
18252             this.hiddenField.dom.value = vv;
18253             
18254             this.lastSelectionText = dv;
18255             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18256             this.value = vv;
18257             return;
18258         }
18259         // no hidden field.. - we store the value in 'value', but still display
18260         // display field!!!!
18261         this.lastSelectionText = dv;
18262         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18263         this.value = vv;
18264         
18265         
18266         
18267     },
18268     // private
18269     reset : function(){
18270         // overridden so that last data is reset..
18271         
18272         if(this.multiple){
18273             this.clearItem();
18274             return;
18275         }
18276         
18277         this.setValue(this.originalValue);
18278         //this.clearInvalid();
18279         this.lastData = false;
18280         if (this.view) {
18281             this.view.clearSelections();
18282         }
18283         
18284         this.validate();
18285     },
18286     // private
18287     findRecord : function(prop, value){
18288         var record;
18289         if(this.store.getCount() > 0){
18290             this.store.each(function(r){
18291                 if(r.data[prop] == value){
18292                     record = r;
18293                     return false;
18294                 }
18295                 return true;
18296             });
18297         }
18298         return record;
18299     },
18300     
18301     getName: function()
18302     {
18303         // returns hidden if it's set..
18304         if (!this.rendered) {return ''};
18305         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18306         
18307     },
18308     // private
18309     onViewMove : function(e, t){
18310         this.inKeyMode = false;
18311     },
18312
18313     // private
18314     onViewOver : function(e, t){
18315         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18316             return;
18317         }
18318         var item = this.view.findItemFromChild(t);
18319         
18320         if(item){
18321             var index = this.view.indexOf(item);
18322             this.select(index, false);
18323         }
18324     },
18325
18326     // private
18327     onViewClick : function(view, doFocus, el, e)
18328     {
18329         var index = this.view.getSelectedIndexes()[0];
18330         
18331         var r = this.store.getAt(index);
18332         
18333         if(this.tickable){
18334             
18335             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18336                 return;
18337             }
18338             
18339             var rm = false;
18340             var _this = this;
18341             
18342             Roo.each(this.tickItems, function(v,k){
18343                 
18344                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18345                     Roo.log(v);
18346                     _this.tickItems.splice(k, 1);
18347                     
18348                     if(typeof(e) == 'undefined' && view == false){
18349                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18350                     }
18351                     
18352                     rm = true;
18353                     return;
18354                 }
18355             });
18356             
18357             if(rm){
18358                 return;
18359             }
18360             
18361             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18362                 this.tickItems.push(r.data);
18363             }
18364             
18365             if(typeof(e) == 'undefined' && view == false){
18366                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18367             }
18368                     
18369             return;
18370         }
18371         
18372         if(r){
18373             this.onSelect(r, index);
18374         }
18375         if(doFocus !== false && !this.blockFocus){
18376             this.inputEl().focus();
18377         }
18378     },
18379
18380     // private
18381     restrictHeight : function(){
18382         //this.innerList.dom.style.height = '';
18383         //var inner = this.innerList.dom;
18384         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18385         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18386         //this.list.beginUpdate();
18387         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18388         this.list.alignTo(this.inputEl(), this.listAlign);
18389         this.list.alignTo(this.inputEl(), this.listAlign);
18390         //this.list.endUpdate();
18391     },
18392
18393     // private
18394     onEmptyResults : function(){
18395         
18396         if(this.tickable && this.editable){
18397             this.hasFocus = false;
18398             this.restrictHeight();
18399             return;
18400         }
18401         
18402         this.collapse();
18403     },
18404
18405     /**
18406      * Returns true if the dropdown list is expanded, else false.
18407      */
18408     isExpanded : function(){
18409         return this.list.isVisible();
18410     },
18411
18412     /**
18413      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18414      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18415      * @param {String} value The data value of the item to select
18416      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18417      * selected item if it is not currently in view (defaults to true)
18418      * @return {Boolean} True if the value matched an item in the list, else false
18419      */
18420     selectByValue : function(v, scrollIntoView){
18421         if(v !== undefined && v !== null){
18422             var r = this.findRecord(this.valueField || this.displayField, v);
18423             if(r){
18424                 this.select(this.store.indexOf(r), scrollIntoView);
18425                 return true;
18426             }
18427         }
18428         return false;
18429     },
18430
18431     /**
18432      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18433      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18434      * @param {Number} index The zero-based index of the list item to select
18435      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18436      * selected item if it is not currently in view (defaults to true)
18437      */
18438     select : function(index, scrollIntoView){
18439         this.selectedIndex = index;
18440         this.view.select(index);
18441         if(scrollIntoView !== false){
18442             var el = this.view.getNode(index);
18443             /*
18444              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18445              */
18446             if(el){
18447                 this.list.scrollChildIntoView(el, false);
18448             }
18449         }
18450     },
18451
18452     // private
18453     selectNext : function(){
18454         var ct = this.store.getCount();
18455         if(ct > 0){
18456             if(this.selectedIndex == -1){
18457                 this.select(0);
18458             }else if(this.selectedIndex < ct-1){
18459                 this.select(this.selectedIndex+1);
18460             }
18461         }
18462     },
18463
18464     // private
18465     selectPrev : function(){
18466         var ct = this.store.getCount();
18467         if(ct > 0){
18468             if(this.selectedIndex == -1){
18469                 this.select(0);
18470             }else if(this.selectedIndex != 0){
18471                 this.select(this.selectedIndex-1);
18472             }
18473         }
18474     },
18475
18476     // private
18477     onKeyUp : function(e){
18478         if(this.editable !== false && !e.isSpecialKey()){
18479             this.lastKey = e.getKey();
18480             this.dqTask.delay(this.queryDelay);
18481         }
18482     },
18483
18484     // private
18485     validateBlur : function(){
18486         return !this.list || !this.list.isVisible();   
18487     },
18488
18489     // private
18490     initQuery : function(){
18491         
18492         var v = this.getRawValue();
18493         
18494         if(this.tickable && this.editable){
18495             v = this.tickableInputEl().getValue();
18496         }
18497         
18498         this.doQuery(v);
18499     },
18500
18501     // private
18502     doForce : function(){
18503         if(this.inputEl().dom.value.length > 0){
18504             this.inputEl().dom.value =
18505                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18506              
18507         }
18508     },
18509
18510     /**
18511      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18512      * query allowing the query action to be canceled if needed.
18513      * @param {String} query The SQL query to execute
18514      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18515      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18516      * saved in the current store (defaults to false)
18517      */
18518     doQuery : function(q, forceAll){
18519         
18520         if(q === undefined || q === null){
18521             q = '';
18522         }
18523         var qe = {
18524             query: q,
18525             forceAll: forceAll,
18526             combo: this,
18527             cancel:false
18528         };
18529         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18530             return false;
18531         }
18532         q = qe.query;
18533         
18534         forceAll = qe.forceAll;
18535         if(forceAll === true || (q.length >= this.minChars)){
18536             
18537             this.hasQuery = true;
18538             
18539             if(this.lastQuery != q || this.alwaysQuery){
18540                 this.lastQuery = q;
18541                 if(this.mode == 'local'){
18542                     this.selectedIndex = -1;
18543                     if(forceAll){
18544                         this.store.clearFilter();
18545                     }else{
18546                         
18547                         if(this.specialFilter){
18548                             this.fireEvent('specialfilter', this);
18549                             this.onLoad();
18550                             return;
18551                         }
18552                         
18553                         this.store.filter(this.displayField, q);
18554                     }
18555                     
18556                     this.store.fireEvent("datachanged", this.store);
18557                     
18558                     this.onLoad();
18559                     
18560                     
18561                 }else{
18562                     
18563                     this.store.baseParams[this.queryParam] = q;
18564                     
18565                     var options = {params : this.getParams(q)};
18566                     
18567                     if(this.loadNext){
18568                         options.add = true;
18569                         options.params.start = this.page * this.pageSize;
18570                     }
18571                     
18572                     this.store.load(options);
18573                     
18574                     /*
18575                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18576                      *  we should expand the list on onLoad
18577                      *  so command out it
18578                      */
18579 //                    this.expand();
18580                 }
18581             }else{
18582                 this.selectedIndex = -1;
18583                 this.onLoad();   
18584             }
18585         }
18586         
18587         this.loadNext = false;
18588     },
18589     
18590     // private
18591     getParams : function(q){
18592         var p = {};
18593         //p[this.queryParam] = q;
18594         
18595         if(this.pageSize){
18596             p.start = 0;
18597             p.limit = this.pageSize;
18598         }
18599         return p;
18600     },
18601
18602     /**
18603      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18604      */
18605     collapse : function(){
18606         if(!this.isExpanded()){
18607             return;
18608         }
18609         
18610         this.list.hide();
18611         
18612         this.hasFocus = false;
18613         
18614         if(this.tickable){
18615             this.okBtn.hide();
18616             this.cancelBtn.hide();
18617             this.trigger.show();
18618             
18619             if(this.editable){
18620                 this.tickableInputEl().dom.value = '';
18621                 this.tickableInputEl().blur();
18622             }
18623             
18624         }
18625         
18626         Roo.get(document).un('mousedown', this.collapseIf, this);
18627         Roo.get(document).un('mousewheel', this.collapseIf, this);
18628         if (!this.editable) {
18629             Roo.get(document).un('keydown', this.listKeyPress, this);
18630         }
18631         this.fireEvent('collapse', this);
18632         
18633         this.validate();
18634     },
18635
18636     // private
18637     collapseIf : function(e){
18638         var in_combo  = e.within(this.el);
18639         var in_list =  e.within(this.list);
18640         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18641         
18642         if (in_combo || in_list || is_list) {
18643             //e.stopPropagation();
18644             return;
18645         }
18646         
18647         if(this.tickable){
18648             this.onTickableFooterButtonClick(e, false, false);
18649         }
18650
18651         this.collapse();
18652         
18653     },
18654
18655     /**
18656      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18657      */
18658     expand : function(){
18659        
18660         if(this.isExpanded() || !this.hasFocus){
18661             return;
18662         }
18663         
18664         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18665         this.list.setWidth(lw);
18666         
18667         Roo.log('expand');
18668         
18669         this.list.show();
18670         
18671         this.restrictHeight();
18672         
18673         if(this.tickable){
18674             
18675             this.tickItems = Roo.apply([], this.item);
18676             
18677             this.okBtn.show();
18678             this.cancelBtn.show();
18679             this.trigger.hide();
18680             
18681             if(this.editable){
18682                 this.tickableInputEl().focus();
18683             }
18684             
18685         }
18686         
18687         Roo.get(document).on('mousedown', this.collapseIf, this);
18688         Roo.get(document).on('mousewheel', this.collapseIf, this);
18689         if (!this.editable) {
18690             Roo.get(document).on('keydown', this.listKeyPress, this);
18691         }
18692         
18693         this.fireEvent('expand', this);
18694     },
18695
18696     // private
18697     // Implements the default empty TriggerField.onTriggerClick function
18698     onTriggerClick : function(e)
18699     {
18700         Roo.log('trigger click');
18701         
18702         if(this.disabled || !this.triggerList){
18703             return;
18704         }
18705         
18706         this.page = 0;
18707         this.loadNext = false;
18708         
18709         if(this.isExpanded()){
18710             this.collapse();
18711             if (!this.blockFocus) {
18712                 this.inputEl().focus();
18713             }
18714             
18715         }else {
18716             this.hasFocus = true;
18717             if(this.triggerAction == 'all') {
18718                 this.doQuery(this.allQuery, true);
18719             } else {
18720                 this.doQuery(this.getRawValue());
18721             }
18722             if (!this.blockFocus) {
18723                 this.inputEl().focus();
18724             }
18725         }
18726     },
18727     
18728     onTickableTriggerClick : function(e)
18729     {
18730         if(this.disabled){
18731             return;
18732         }
18733         
18734         this.page = 0;
18735         this.loadNext = false;
18736         this.hasFocus = true;
18737         
18738         if(this.triggerAction == 'all') {
18739             this.doQuery(this.allQuery, true);
18740         } else {
18741             this.doQuery(this.getRawValue());
18742         }
18743     },
18744     
18745     onSearchFieldClick : function(e)
18746     {
18747         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18748             this.onTickableFooterButtonClick(e, false, false);
18749             return;
18750         }
18751         
18752         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18753             return;
18754         }
18755         
18756         this.page = 0;
18757         this.loadNext = false;
18758         this.hasFocus = true;
18759         
18760         if(this.triggerAction == 'all') {
18761             this.doQuery(this.allQuery, true);
18762         } else {
18763             this.doQuery(this.getRawValue());
18764         }
18765     },
18766     
18767     listKeyPress : function(e)
18768     {
18769         //Roo.log('listkeypress');
18770         // scroll to first matching element based on key pres..
18771         if (e.isSpecialKey()) {
18772             return false;
18773         }
18774         var k = String.fromCharCode(e.getKey()).toUpperCase();
18775         //Roo.log(k);
18776         var match  = false;
18777         var csel = this.view.getSelectedNodes();
18778         var cselitem = false;
18779         if (csel.length) {
18780             var ix = this.view.indexOf(csel[0]);
18781             cselitem  = this.store.getAt(ix);
18782             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18783                 cselitem = false;
18784             }
18785             
18786         }
18787         
18788         this.store.each(function(v) { 
18789             if (cselitem) {
18790                 // start at existing selection.
18791                 if (cselitem.id == v.id) {
18792                     cselitem = false;
18793                 }
18794                 return true;
18795             }
18796                 
18797             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18798                 match = this.store.indexOf(v);
18799                 return false;
18800             }
18801             return true;
18802         }, this);
18803         
18804         if (match === false) {
18805             return true; // no more action?
18806         }
18807         // scroll to?
18808         this.view.select(match);
18809         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18810         sn.scrollIntoView(sn.dom.parentNode, false);
18811     },
18812     
18813     onViewScroll : function(e, t){
18814         
18815         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){
18816             return;
18817         }
18818         
18819         this.hasQuery = true;
18820         
18821         this.loading = this.list.select('.loading', true).first();
18822         
18823         if(this.loading === null){
18824             this.list.createChild({
18825                 tag: 'div',
18826                 cls: 'loading roo-select2-more-results roo-select2-active',
18827                 html: 'Loading more results...'
18828             });
18829             
18830             this.loading = this.list.select('.loading', true).first();
18831             
18832             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18833             
18834             this.loading.hide();
18835         }
18836         
18837         this.loading.show();
18838         
18839         var _combo = this;
18840         
18841         this.page++;
18842         this.loadNext = true;
18843         
18844         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18845         
18846         return;
18847     },
18848     
18849     addItem : function(o)
18850     {   
18851         var dv = ''; // display value
18852         
18853         if (this.displayField) {
18854             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18855         } else {
18856             // this is an error condition!!!
18857             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18858         }
18859         
18860         if(!dv.length){
18861             return;
18862         }
18863         
18864         var choice = this.choices.createChild({
18865             tag: 'li',
18866             cls: 'roo-select2-search-choice',
18867             cn: [
18868                 {
18869                     tag: 'div',
18870                     html: dv
18871                 },
18872                 {
18873                     tag: 'a',
18874                     href: '#',
18875                     cls: 'roo-select2-search-choice-close fa fa-times',
18876                     tabindex: '-1'
18877                 }
18878             ]
18879             
18880         }, this.searchField);
18881         
18882         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18883         
18884         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18885         
18886         this.item.push(o);
18887         
18888         this.lastData = o;
18889         
18890         this.syncValue();
18891         
18892         this.inputEl().dom.value = '';
18893         
18894         this.validate();
18895     },
18896     
18897     onRemoveItem : function(e, _self, o)
18898     {
18899         e.preventDefault();
18900         
18901         this.lastItem = Roo.apply([], this.item);
18902         
18903         var index = this.item.indexOf(o.data) * 1;
18904         
18905         if( index < 0){
18906             Roo.log('not this item?!');
18907             return;
18908         }
18909         
18910         this.item.splice(index, 1);
18911         o.item.remove();
18912         
18913         this.syncValue();
18914         
18915         this.fireEvent('remove', this, e);
18916         
18917         this.validate();
18918         
18919     },
18920     
18921     syncValue : function()
18922     {
18923         if(!this.item.length){
18924             this.clearValue();
18925             return;
18926         }
18927             
18928         var value = [];
18929         var _this = this;
18930         Roo.each(this.item, function(i){
18931             if(_this.valueField){
18932                 value.push(i[_this.valueField]);
18933                 return;
18934             }
18935
18936             value.push(i);
18937         });
18938
18939         this.value = value.join(',');
18940
18941         if(this.hiddenField){
18942             this.hiddenField.dom.value = this.value;
18943         }
18944         
18945         this.store.fireEvent("datachanged", this.store);
18946         
18947         this.validate();
18948     },
18949     
18950     clearItem : function()
18951     {
18952         if(!this.multiple){
18953             return;
18954         }
18955         
18956         this.item = [];
18957         
18958         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18959            c.remove();
18960         });
18961         
18962         this.syncValue();
18963         
18964         this.validate();
18965         
18966         if(this.tickable && !Roo.isTouch){
18967             this.view.refresh();
18968         }
18969     },
18970     
18971     inputEl: function ()
18972     {
18973         if(Roo.isIOS && this.useNativeIOS){
18974             return this.el.select('select.roo-ios-select', true).first();
18975         }
18976         
18977         if(Roo.isTouch && this.mobileTouchView){
18978             return this.el.select('input.form-control',true).first();
18979         }
18980         
18981         if(this.tickable){
18982             return this.searchField;
18983         }
18984         
18985         return this.el.select('input.form-control',true).first();
18986     },
18987     
18988     onTickableFooterButtonClick : function(e, btn, el)
18989     {
18990         e.preventDefault();
18991         
18992         this.lastItem = Roo.apply([], this.item);
18993         
18994         if(btn && btn.name == 'cancel'){
18995             this.tickItems = Roo.apply([], this.item);
18996             this.collapse();
18997             return;
18998         }
18999         
19000         this.clearItem();
19001         
19002         var _this = this;
19003         
19004         Roo.each(this.tickItems, function(o){
19005             _this.addItem(o);
19006         });
19007         
19008         this.collapse();
19009         
19010     },
19011     
19012     validate : function()
19013     {
19014         if(this.getVisibilityEl().hasClass('hidden')){
19015             return true;
19016         }
19017         
19018         var v = this.getRawValue();
19019         
19020         if(this.multiple){
19021             v = this.getValue();
19022         }
19023         
19024         if(this.disabled || this.allowBlank || v.length){
19025             this.markValid();
19026             return true;
19027         }
19028         
19029         this.markInvalid();
19030         return false;
19031     },
19032     
19033     tickableInputEl : function()
19034     {
19035         if(!this.tickable || !this.editable){
19036             return this.inputEl();
19037         }
19038         
19039         return this.inputEl().select('.roo-select2-search-field-input', true).first();
19040     },
19041     
19042     
19043     getAutoCreateTouchView : function()
19044     {
19045         var id = Roo.id();
19046         
19047         var cfg = {
19048             cls: 'form-group' //input-group
19049         };
19050         
19051         var input =  {
19052             tag: 'input',
19053             id : id,
19054             type : this.inputType,
19055             cls : 'form-control x-combo-noedit',
19056             autocomplete: 'new-password',
19057             placeholder : this.placeholder || '',
19058             readonly : true
19059         };
19060         
19061         if (this.name) {
19062             input.name = this.name;
19063         }
19064         
19065         if (this.size) {
19066             input.cls += ' input-' + this.size;
19067         }
19068         
19069         if (this.disabled) {
19070             input.disabled = true;
19071         }
19072         
19073         var inputblock = {
19074             cls : 'roo-combobox-wrap',
19075             cn : [
19076                 input
19077             ]
19078         };
19079         
19080         if(this.before){
19081             inputblock.cls += ' input-group';
19082             
19083             inputblock.cn.unshift({
19084                 tag :'span',
19085                 cls : 'input-group-addon input-group-prepend input-group-text',
19086                 html : this.before
19087             });
19088         }
19089         
19090         if(this.removable && !this.multiple){
19091             inputblock.cls += ' roo-removable';
19092             
19093             inputblock.cn.push({
19094                 tag: 'button',
19095                 html : 'x',
19096                 cls : 'roo-combo-removable-btn close'
19097             });
19098         }
19099
19100         if(this.hasFeedback && !this.allowBlank){
19101             
19102             inputblock.cls += ' has-feedback';
19103             
19104             inputblock.cn.push({
19105                 tag: 'span',
19106                 cls: 'glyphicon form-control-feedback'
19107             });
19108             
19109         }
19110         
19111         if (this.after) {
19112             
19113             inputblock.cls += (this.before) ? '' : ' input-group';
19114             
19115             inputblock.cn.push({
19116                 tag :'span',
19117                 cls : 'input-group-addon input-group-append input-group-text',
19118                 html : this.after
19119             });
19120         }
19121
19122         
19123         var ibwrap = inputblock;
19124         
19125         if(this.multiple){
19126             ibwrap = {
19127                 tag: 'ul',
19128                 cls: 'roo-select2-choices',
19129                 cn:[
19130                     {
19131                         tag: 'li',
19132                         cls: 'roo-select2-search-field',
19133                         cn: [
19134
19135                             inputblock
19136                         ]
19137                     }
19138                 ]
19139             };
19140         
19141             
19142         }
19143         
19144         var combobox = {
19145             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19146             cn: [
19147                 {
19148                     tag: 'input',
19149                     type : 'hidden',
19150                     cls: 'form-hidden-field'
19151                 },
19152                 ibwrap
19153             ]
19154         };
19155         
19156         if(!this.multiple && this.showToggleBtn){
19157             
19158             var caret = {
19159                 cls: 'caret'
19160             };
19161             
19162             if (this.caret != false) {
19163                 caret = {
19164                      tag: 'i',
19165                      cls: 'fa fa-' + this.caret
19166                 };
19167                 
19168             }
19169             
19170             combobox.cn.push({
19171                 tag :'span',
19172                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19173                 cn : [
19174                     Roo.bootstrap.version == 3 ? caret : '',
19175                     {
19176                         tag: 'span',
19177                         cls: 'combobox-clear',
19178                         cn  : [
19179                             {
19180                                 tag : 'i',
19181                                 cls: 'icon-remove'
19182                             }
19183                         ]
19184                     }
19185                 ]
19186
19187             })
19188         }
19189         
19190         if(this.multiple){
19191             combobox.cls += ' roo-select2-container-multi';
19192         }
19193         
19194         var required =  this.allowBlank ?  {
19195                     tag : 'i',
19196                     style: 'display: none'
19197                 } : {
19198                    tag : 'i',
19199                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19200                    tooltip : 'This field is required'
19201                 };
19202         
19203         var align = this.labelAlign || this.parentLabelAlign();
19204         
19205         if (align ==='left' && this.fieldLabel.length) {
19206
19207             cfg.cn = [
19208                 required,
19209                 {
19210                     tag: 'label',
19211                     cls : 'control-label col-form-label',
19212                     html : this.fieldLabel
19213
19214                 },
19215                 {
19216                     cls : 'roo-combobox-wrap ', 
19217                     cn: [
19218                         combobox
19219                     ]
19220                 }
19221             ];
19222             
19223             var labelCfg = cfg.cn[1];
19224             var contentCfg = cfg.cn[2];
19225             
19226
19227             if(this.indicatorpos == 'right'){
19228                 cfg.cn = [
19229                     {
19230                         tag: 'label',
19231                         'for' :  id,
19232                         cls : 'control-label col-form-label',
19233                         cn : [
19234                             {
19235                                 tag : 'span',
19236                                 html : this.fieldLabel
19237                             },
19238                             required
19239                         ]
19240                     },
19241                     {
19242                         cls : "roo-combobox-wrap ",
19243                         cn: [
19244                             combobox
19245                         ]
19246                     }
19247
19248                 ];
19249                 
19250                 labelCfg = cfg.cn[0];
19251                 contentCfg = cfg.cn[1];
19252             }
19253             
19254            
19255             
19256             if(this.labelWidth > 12){
19257                 labelCfg.style = "width: " + this.labelWidth + 'px';
19258             }
19259            
19260             if(this.labelWidth < 13 && this.labelmd == 0){
19261                 this.labelmd = this.labelWidth;
19262             }
19263             
19264             if(this.labellg > 0){
19265                 labelCfg.cls += ' col-lg-' + this.labellg;
19266                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19267             }
19268             
19269             if(this.labelmd > 0){
19270                 labelCfg.cls += ' col-md-' + this.labelmd;
19271                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19272             }
19273             
19274             if(this.labelsm > 0){
19275                 labelCfg.cls += ' col-sm-' + this.labelsm;
19276                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19277             }
19278             
19279             if(this.labelxs > 0){
19280                 labelCfg.cls += ' col-xs-' + this.labelxs;
19281                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19282             }
19283                 
19284                 
19285         } else if ( this.fieldLabel.length) {
19286             cfg.cn = [
19287                required,
19288                 {
19289                     tag: 'label',
19290                     cls : 'control-label',
19291                     html : this.fieldLabel
19292
19293                 },
19294                 {
19295                     cls : '', 
19296                     cn: [
19297                         combobox
19298                     ]
19299                 }
19300             ];
19301             
19302             if(this.indicatorpos == 'right'){
19303                 cfg.cn = [
19304                     {
19305                         tag: 'label',
19306                         cls : 'control-label',
19307                         html : this.fieldLabel,
19308                         cn : [
19309                             required
19310                         ]
19311                     },
19312                     {
19313                         cls : '', 
19314                         cn: [
19315                             combobox
19316                         ]
19317                     }
19318                 ];
19319             }
19320         } else {
19321             cfg.cn = combobox;    
19322         }
19323         
19324         
19325         var settings = this;
19326         
19327         ['xs','sm','md','lg'].map(function(size){
19328             if (settings[size]) {
19329                 cfg.cls += ' col-' + size + '-' + settings[size];
19330             }
19331         });
19332         
19333         return cfg;
19334     },
19335     
19336     initTouchView : function()
19337     {
19338         this.renderTouchView();
19339         
19340         this.touchViewEl.on('scroll', function(){
19341             this.el.dom.scrollTop = 0;
19342         }, this);
19343         
19344         this.originalValue = this.getValue();
19345         
19346         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19347         
19348         this.inputEl().on("click", this.showTouchView, this);
19349         if (this.triggerEl) {
19350             this.triggerEl.on("click", this.showTouchView, this);
19351         }
19352         
19353         
19354         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19355         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19356         
19357         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19358         
19359         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19360         this.store.on('load', this.onTouchViewLoad, this);
19361         this.store.on('loadexception', this.onTouchViewLoadException, this);
19362         
19363         if(this.hiddenName){
19364             
19365             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19366             
19367             this.hiddenField.dom.value =
19368                 this.hiddenValue !== undefined ? this.hiddenValue :
19369                 this.value !== undefined ? this.value : '';
19370         
19371             this.el.dom.removeAttribute('name');
19372             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19373         }
19374         
19375         if(this.multiple){
19376             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19377             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19378         }
19379         
19380         if(this.removable && !this.multiple){
19381             var close = this.closeTriggerEl();
19382             if(close){
19383                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19384                 close.on('click', this.removeBtnClick, this, close);
19385             }
19386         }
19387         /*
19388          * fix the bug in Safari iOS8
19389          */
19390         this.inputEl().on("focus", function(e){
19391             document.activeElement.blur();
19392         }, this);
19393         
19394         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19395         
19396         return;
19397         
19398         
19399     },
19400     
19401     renderTouchView : function()
19402     {
19403         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19404         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19405         
19406         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19407         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19408         
19409         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19410         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19411         this.touchViewBodyEl.setStyle('overflow', 'auto');
19412         
19413         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19414         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19415         
19416         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19417         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19418         
19419     },
19420     
19421     showTouchView : function()
19422     {
19423         if(this.disabled){
19424             return;
19425         }
19426         
19427         this.touchViewHeaderEl.hide();
19428
19429         if(this.modalTitle.length){
19430             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19431             this.touchViewHeaderEl.show();
19432         }
19433
19434         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19435         this.touchViewEl.show();
19436
19437         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19438         
19439         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19440         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19441
19442         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19443
19444         if(this.modalTitle.length){
19445             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19446         }
19447         
19448         this.touchViewBodyEl.setHeight(bodyHeight);
19449
19450         if(this.animate){
19451             var _this = this;
19452             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19453         }else{
19454             this.touchViewEl.addClass(['in','show']);
19455         }
19456         
19457         if(this._touchViewMask){
19458             Roo.get(document.body).addClass("x-body-masked");
19459             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19460             this._touchViewMask.setStyle('z-index', 10000);
19461             this._touchViewMask.addClass('show');
19462         }
19463         
19464         this.doTouchViewQuery();
19465         
19466     },
19467     
19468     hideTouchView : function()
19469     {
19470         this.touchViewEl.removeClass(['in','show']);
19471
19472         if(this.animate){
19473             var _this = this;
19474             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19475         }else{
19476             this.touchViewEl.setStyle('display', 'none');
19477         }
19478         
19479         if(this._touchViewMask){
19480             this._touchViewMask.removeClass('show');
19481             Roo.get(document.body).removeClass("x-body-masked");
19482         }
19483     },
19484     
19485     setTouchViewValue : function()
19486     {
19487         if(this.multiple){
19488             this.clearItem();
19489         
19490             var _this = this;
19491
19492             Roo.each(this.tickItems, function(o){
19493                 this.addItem(o);
19494             }, this);
19495         }
19496         
19497         this.hideTouchView();
19498     },
19499     
19500     doTouchViewQuery : function()
19501     {
19502         var qe = {
19503             query: '',
19504             forceAll: true,
19505             combo: this,
19506             cancel:false
19507         };
19508         
19509         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19510             return false;
19511         }
19512         
19513         if(!this.alwaysQuery || this.mode == 'local'){
19514             this.onTouchViewLoad();
19515             return;
19516         }
19517         
19518         this.store.load();
19519     },
19520     
19521     onTouchViewBeforeLoad : function(combo,opts)
19522     {
19523         return;
19524     },
19525
19526     // private
19527     onTouchViewLoad : function()
19528     {
19529         if(this.store.getCount() < 1){
19530             this.onTouchViewEmptyResults();
19531             return;
19532         }
19533         
19534         this.clearTouchView();
19535         
19536         var rawValue = this.getRawValue();
19537         
19538         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19539         
19540         this.tickItems = [];
19541         
19542         this.store.data.each(function(d, rowIndex){
19543             var row = this.touchViewListGroup.createChild(template);
19544             
19545             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19546                 row.addClass(d.data.cls);
19547             }
19548             
19549             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19550                 var cfg = {
19551                     data : d.data,
19552                     html : d.data[this.displayField]
19553                 };
19554                 
19555                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19556                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19557                 }
19558             }
19559             row.removeClass('selected');
19560             if(!this.multiple && this.valueField &&
19561                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19562             {
19563                 // radio buttons..
19564                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19565                 row.addClass('selected');
19566             }
19567             
19568             if(this.multiple && this.valueField &&
19569                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19570             {
19571                 
19572                 // checkboxes...
19573                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19574                 this.tickItems.push(d.data);
19575             }
19576             
19577             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19578             
19579         }, this);
19580         
19581         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19582         
19583         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19584
19585         if(this.modalTitle.length){
19586             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19587         }
19588
19589         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19590         
19591         if(this.mobile_restrict_height && listHeight < bodyHeight){
19592             this.touchViewBodyEl.setHeight(listHeight);
19593         }
19594         
19595         var _this = this;
19596         
19597         if(firstChecked && listHeight > bodyHeight){
19598             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19599         }
19600         
19601     },
19602     
19603     onTouchViewLoadException : function()
19604     {
19605         this.hideTouchView();
19606     },
19607     
19608     onTouchViewEmptyResults : function()
19609     {
19610         this.clearTouchView();
19611         
19612         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19613         
19614         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19615         
19616     },
19617     
19618     clearTouchView : function()
19619     {
19620         this.touchViewListGroup.dom.innerHTML = '';
19621     },
19622     
19623     onTouchViewClick : function(e, el, o)
19624     {
19625         e.preventDefault();
19626         
19627         var row = o.row;
19628         var rowIndex = o.rowIndex;
19629         
19630         var r = this.store.getAt(rowIndex);
19631         
19632         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19633             
19634             if(!this.multiple){
19635                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19636                     c.dom.removeAttribute('checked');
19637                 }, this);
19638
19639                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19640
19641                 this.setFromData(r.data);
19642
19643                 var close = this.closeTriggerEl();
19644
19645                 if(close){
19646                     close.show();
19647                 }
19648
19649                 this.hideTouchView();
19650
19651                 this.fireEvent('select', this, r, rowIndex);
19652
19653                 return;
19654             }
19655
19656             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19657                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19658                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19659                 return;
19660             }
19661
19662             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19663             this.addItem(r.data);
19664             this.tickItems.push(r.data);
19665         }
19666     },
19667     
19668     getAutoCreateNativeIOS : function()
19669     {
19670         var cfg = {
19671             cls: 'form-group' //input-group,
19672         };
19673         
19674         var combobox =  {
19675             tag: 'select',
19676             cls : 'roo-ios-select'
19677         };
19678         
19679         if (this.name) {
19680             combobox.name = this.name;
19681         }
19682         
19683         if (this.disabled) {
19684             combobox.disabled = true;
19685         }
19686         
19687         var settings = this;
19688         
19689         ['xs','sm','md','lg'].map(function(size){
19690             if (settings[size]) {
19691                 cfg.cls += ' col-' + size + '-' + settings[size];
19692             }
19693         });
19694         
19695         cfg.cn = combobox;
19696         
19697         return cfg;
19698         
19699     },
19700     
19701     initIOSView : function()
19702     {
19703         this.store.on('load', this.onIOSViewLoad, this);
19704         
19705         return;
19706     },
19707     
19708     onIOSViewLoad : function()
19709     {
19710         if(this.store.getCount() < 1){
19711             return;
19712         }
19713         
19714         this.clearIOSView();
19715         
19716         if(this.allowBlank) {
19717             
19718             var default_text = '-- SELECT --';
19719             
19720             if(this.placeholder.length){
19721                 default_text = this.placeholder;
19722             }
19723             
19724             if(this.emptyTitle.length){
19725                 default_text += ' - ' + this.emptyTitle + ' -';
19726             }
19727             
19728             var opt = this.inputEl().createChild({
19729                 tag: 'option',
19730                 value : 0,
19731                 html : default_text
19732             });
19733             
19734             var o = {};
19735             o[this.valueField] = 0;
19736             o[this.displayField] = default_text;
19737             
19738             this.ios_options.push({
19739                 data : o,
19740                 el : opt
19741             });
19742             
19743         }
19744         
19745         this.store.data.each(function(d, rowIndex){
19746             
19747             var html = '';
19748             
19749             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19750                 html = d.data[this.displayField];
19751             }
19752             
19753             var value = '';
19754             
19755             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19756                 value = d.data[this.valueField];
19757             }
19758             
19759             var option = {
19760                 tag: 'option',
19761                 value : value,
19762                 html : html
19763             };
19764             
19765             if(this.value == d.data[this.valueField]){
19766                 option['selected'] = true;
19767             }
19768             
19769             var opt = this.inputEl().createChild(option);
19770             
19771             this.ios_options.push({
19772                 data : d.data,
19773                 el : opt
19774             });
19775             
19776         }, this);
19777         
19778         this.inputEl().on('change', function(){
19779            this.fireEvent('select', this);
19780         }, this);
19781         
19782     },
19783     
19784     clearIOSView: function()
19785     {
19786         this.inputEl().dom.innerHTML = '';
19787         
19788         this.ios_options = [];
19789     },
19790     
19791     setIOSValue: function(v)
19792     {
19793         this.value = v;
19794         
19795         if(!this.ios_options){
19796             return;
19797         }
19798         
19799         Roo.each(this.ios_options, function(opts){
19800            
19801            opts.el.dom.removeAttribute('selected');
19802            
19803            if(opts.data[this.valueField] != v){
19804                return;
19805            }
19806            
19807            opts.el.dom.setAttribute('selected', true);
19808            
19809         }, this);
19810     }
19811
19812     /** 
19813     * @cfg {Boolean} grow 
19814     * @hide 
19815     */
19816     /** 
19817     * @cfg {Number} growMin 
19818     * @hide 
19819     */
19820     /** 
19821     * @cfg {Number} growMax 
19822     * @hide 
19823     */
19824     /**
19825      * @hide
19826      * @method autoSize
19827      */
19828 });
19829
19830 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19831     
19832     header : {
19833         tag: 'div',
19834         cls: 'modal-header',
19835         cn: [
19836             {
19837                 tag: 'h4',
19838                 cls: 'modal-title'
19839             }
19840         ]
19841     },
19842     
19843     body : {
19844         tag: 'div',
19845         cls: 'modal-body',
19846         cn: [
19847             {
19848                 tag: 'ul',
19849                 cls: 'list-group'
19850             }
19851         ]
19852     },
19853     
19854     listItemRadio : {
19855         tag: 'li',
19856         cls: 'list-group-item',
19857         cn: [
19858             {
19859                 tag: 'span',
19860                 cls: 'roo-combobox-list-group-item-value'
19861             },
19862             {
19863                 tag: 'div',
19864                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19865                 cn: [
19866                     {
19867                         tag: 'input',
19868                         type: 'radio'
19869                     },
19870                     {
19871                         tag: 'label'
19872                     }
19873                 ]
19874             }
19875         ]
19876     },
19877     
19878     listItemCheckbox : {
19879         tag: 'li',
19880         cls: 'list-group-item',
19881         cn: [
19882             {
19883                 tag: 'span',
19884                 cls: 'roo-combobox-list-group-item-value'
19885             },
19886             {
19887                 tag: 'div',
19888                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19889                 cn: [
19890                     {
19891                         tag: 'input',
19892                         type: 'checkbox'
19893                     },
19894                     {
19895                         tag: 'label'
19896                     }
19897                 ]
19898             }
19899         ]
19900     },
19901     
19902     emptyResult : {
19903         tag: 'div',
19904         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19905     },
19906     
19907     footer : {
19908         tag: 'div',
19909         cls: 'modal-footer',
19910         cn: [
19911             {
19912                 tag: 'div',
19913                 cls: 'row',
19914                 cn: [
19915                     {
19916                         tag: 'div',
19917                         cls: 'col-xs-6 text-left',
19918                         cn: {
19919                             tag: 'button',
19920                             cls: 'btn btn-danger roo-touch-view-cancel',
19921                             html: 'Cancel'
19922                         }
19923                     },
19924                     {
19925                         tag: 'div',
19926                         cls: 'col-xs-6 text-right',
19927                         cn: {
19928                             tag: 'button',
19929                             cls: 'btn btn-success roo-touch-view-ok',
19930                             html: 'OK'
19931                         }
19932                     }
19933                 ]
19934             }
19935         ]
19936         
19937     }
19938 });
19939
19940 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19941     
19942     touchViewTemplate : {
19943         tag: 'div',
19944         cls: 'modal fade roo-combobox-touch-view',
19945         cn: [
19946             {
19947                 tag: 'div',
19948                 cls: 'modal-dialog',
19949                 style : 'position:fixed', // we have to fix position....
19950                 cn: [
19951                     {
19952                         tag: 'div',
19953                         cls: 'modal-content',
19954                         cn: [
19955                             Roo.bootstrap.form.ComboBox.header,
19956                             Roo.bootstrap.form.ComboBox.body,
19957                             Roo.bootstrap.form.ComboBox.footer
19958                         ]
19959                     }
19960                 ]
19961             }
19962         ]
19963     }
19964 });/*
19965  * Based on:
19966  * Ext JS Library 1.1.1
19967  * Copyright(c) 2006-2007, Ext JS, LLC.
19968  *
19969  * Originally Released Under LGPL - original licence link has changed is not relivant.
19970  *
19971  * Fork - LGPL
19972  * <script type="text/javascript">
19973  */
19974
19975 /**
19976  * @class Roo.View
19977  * @extends Roo.util.Observable
19978  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19979  * This class also supports single and multi selection modes. <br>
19980  * Create a data model bound view:
19981  <pre><code>
19982  var store = new Roo.data.Store(...);
19983
19984  var view = new Roo.View({
19985     el : "my-element",
19986     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19987  
19988     singleSelect: true,
19989     selectedClass: "ydataview-selected",
19990     store: store
19991  });
19992
19993  // listen for node click?
19994  view.on("click", function(vw, index, node, e){
19995  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19996  });
19997
19998  // load XML data
19999  dataModel.load("foobar.xml");
20000  </code></pre>
20001  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
20002  * <br><br>
20003  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
20004  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
20005  * 
20006  * Note: old style constructor is still suported (container, template, config)
20007  * 
20008  * @constructor
20009  * Create a new View
20010  * @param {Object} config The config object
20011  * 
20012  */
20013 Roo.View = function(config, depreciated_tpl, depreciated_config){
20014     
20015     this.parent = false;
20016     
20017     if (typeof(depreciated_tpl) == 'undefined') {
20018         // new way.. - universal constructor.
20019         Roo.apply(this, config);
20020         this.el  = Roo.get(this.el);
20021     } else {
20022         // old format..
20023         this.el  = Roo.get(config);
20024         this.tpl = depreciated_tpl;
20025         Roo.apply(this, depreciated_config);
20026     }
20027     this.wrapEl  = this.el.wrap().wrap();
20028     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
20029     
20030     
20031     if(typeof(this.tpl) == "string"){
20032         this.tpl = new Roo.Template(this.tpl);
20033     } else {
20034         // support xtype ctors..
20035         this.tpl = new Roo.factory(this.tpl, Roo);
20036     }
20037     
20038     
20039     this.tpl.compile();
20040     
20041     /** @private */
20042     this.addEvents({
20043         /**
20044          * @event beforeclick
20045          * Fires before a click is processed. Returns false to cancel the default action.
20046          * @param {Roo.View} this
20047          * @param {Number} index The index of the target node
20048          * @param {HTMLElement} node The target node
20049          * @param {Roo.EventObject} e The raw event object
20050          */
20051             "beforeclick" : true,
20052         /**
20053          * @event click
20054          * Fires when a template node is clicked.
20055          * @param {Roo.View} this
20056          * @param {Number} index The index of the target node
20057          * @param {HTMLElement} node The target node
20058          * @param {Roo.EventObject} e The raw event object
20059          */
20060             "click" : true,
20061         /**
20062          * @event dblclick
20063          * Fires when a template node is double clicked.
20064          * @param {Roo.View} this
20065          * @param {Number} index The index of the target node
20066          * @param {HTMLElement} node The target node
20067          * @param {Roo.EventObject} e The raw event object
20068          */
20069             "dblclick" : true,
20070         /**
20071          * @event contextmenu
20072          * Fires when a template node is right clicked.
20073          * @param {Roo.View} this
20074          * @param {Number} index The index of the target node
20075          * @param {HTMLElement} node The target node
20076          * @param {Roo.EventObject} e The raw event object
20077          */
20078             "contextmenu" : true,
20079         /**
20080          * @event selectionchange
20081          * Fires when the selected nodes change.
20082          * @param {Roo.View} this
20083          * @param {Array} selections Array of the selected nodes
20084          */
20085             "selectionchange" : true,
20086     
20087         /**
20088          * @event beforeselect
20089          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20090          * @param {Roo.View} this
20091          * @param {HTMLElement} node The node to be selected
20092          * @param {Array} selections Array of currently selected nodes
20093          */
20094             "beforeselect" : true,
20095         /**
20096          * @event preparedata
20097          * Fires on every row to render, to allow you to change the data.
20098          * @param {Roo.View} this
20099          * @param {Object} data to be rendered (change this)
20100          */
20101           "preparedata" : true
20102           
20103           
20104         });
20105
20106
20107
20108     this.el.on({
20109         "click": this.onClick,
20110         "dblclick": this.onDblClick,
20111         "contextmenu": this.onContextMenu,
20112         scope:this
20113     });
20114
20115     this.selections = [];
20116     this.nodes = [];
20117     this.cmp = new Roo.CompositeElementLite([]);
20118     if(this.store){
20119         this.store = Roo.factory(this.store, Roo.data);
20120         this.setStore(this.store, true);
20121     }
20122     
20123     if ( this.footer && this.footer.xtype) {
20124            
20125          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20126         
20127         this.footer.dataSource = this.store;
20128         this.footer.container = fctr;
20129         this.footer = Roo.factory(this.footer, Roo);
20130         fctr.insertFirst(this.el);
20131         
20132         // this is a bit insane - as the paging toolbar seems to detach the el..
20133 //        dom.parentNode.parentNode.parentNode
20134          // they get detached?
20135     }
20136     
20137     
20138     Roo.View.superclass.constructor.call(this);
20139     
20140     
20141 };
20142
20143 Roo.extend(Roo.View, Roo.util.Observable, {
20144     
20145      /**
20146      * @cfg {Roo.data.Store} store Data store to load data from.
20147      */
20148     store : false,
20149     
20150     /**
20151      * @cfg {String|Roo.Element} el The container element.
20152      */
20153     el : '',
20154     
20155     /**
20156      * @cfg {String|Roo.Template} tpl The template used by this View 
20157      */
20158     tpl : false,
20159     /**
20160      * @cfg {String} dataName the named area of the template to use as the data area
20161      *                          Works with domtemplates roo-name="name"
20162      */
20163     dataName: false,
20164     /**
20165      * @cfg {String} selectedClass The css class to add to selected nodes
20166      */
20167     selectedClass : "x-view-selected",
20168      /**
20169      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20170      */
20171     emptyText : "",
20172     
20173     /**
20174      * @cfg {String} text to display on mask (default Loading)
20175      */
20176     mask : false,
20177     /**
20178      * @cfg {Boolean} multiSelect Allow multiple selection
20179      */
20180     multiSelect : false,
20181     /**
20182      * @cfg {Boolean} singleSelect Allow single selection
20183      */
20184     singleSelect:  false,
20185     
20186     /**
20187      * @cfg {Boolean} toggleSelect - selecting 
20188      */
20189     toggleSelect : false,
20190     
20191     /**
20192      * @cfg {Boolean} tickable - selecting 
20193      */
20194     tickable : false,
20195     
20196     /**
20197      * Returns the element this view is bound to.
20198      * @return {Roo.Element}
20199      */
20200     getEl : function(){
20201         return this.wrapEl;
20202     },
20203     
20204     
20205
20206     /**
20207      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20208      */
20209     refresh : function(){
20210         //Roo.log('refresh');
20211         var t = this.tpl;
20212         
20213         // if we are using something like 'domtemplate', then
20214         // the what gets used is:
20215         // t.applySubtemplate(NAME, data, wrapping data..)
20216         // the outer template then get' applied with
20217         //     the store 'extra data'
20218         // and the body get's added to the
20219         //      roo-name="data" node?
20220         //      <span class='roo-tpl-{name}'></span> ?????
20221         
20222         
20223         
20224         this.clearSelections();
20225         this.el.update("");
20226         var html = [];
20227         var records = this.store.getRange();
20228         if(records.length < 1) {
20229             
20230             // is this valid??  = should it render a template??
20231             
20232             this.el.update(this.emptyText);
20233             return;
20234         }
20235         var el = this.el;
20236         if (this.dataName) {
20237             this.el.update(t.apply(this.store.meta)); //????
20238             el = this.el.child('.roo-tpl-' + this.dataName);
20239         }
20240         
20241         for(var i = 0, len = records.length; i < len; i++){
20242             var data = this.prepareData(records[i].data, i, records[i]);
20243             this.fireEvent("preparedata", this, data, i, records[i]);
20244             
20245             var d = Roo.apply({}, data);
20246             
20247             if(this.tickable){
20248                 Roo.apply(d, {'roo-id' : Roo.id()});
20249                 
20250                 var _this = this;
20251             
20252                 Roo.each(this.parent.item, function(item){
20253                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20254                         return;
20255                     }
20256                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20257                 });
20258             }
20259             
20260             html[html.length] = Roo.util.Format.trim(
20261                 this.dataName ?
20262                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20263                     t.apply(d)
20264             );
20265         }
20266         
20267         
20268         
20269         el.update(html.join(""));
20270         this.nodes = el.dom.childNodes;
20271         this.updateIndexes(0);
20272     },
20273     
20274
20275     /**
20276      * Function to override to reformat the data that is sent to
20277      * the template for each node.
20278      * DEPRICATED - use the preparedata event handler.
20279      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20280      * a JSON object for an UpdateManager bound view).
20281      */
20282     prepareData : function(data, index, record)
20283     {
20284         this.fireEvent("preparedata", this, data, index, record);
20285         return data;
20286     },
20287
20288     onUpdate : function(ds, record){
20289         // Roo.log('on update');   
20290         this.clearSelections();
20291         var index = this.store.indexOf(record);
20292         var n = this.nodes[index];
20293         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20294         n.parentNode.removeChild(n);
20295         this.updateIndexes(index, index);
20296     },
20297
20298     
20299     
20300 // --------- FIXME     
20301     onAdd : function(ds, records, index)
20302     {
20303         //Roo.log(['on Add', ds, records, index] );        
20304         this.clearSelections();
20305         if(this.nodes.length == 0){
20306             this.refresh();
20307             return;
20308         }
20309         var n = this.nodes[index];
20310         for(var i = 0, len = records.length; i < len; i++){
20311             var d = this.prepareData(records[i].data, i, records[i]);
20312             if(n){
20313                 this.tpl.insertBefore(n, d);
20314             }else{
20315                 
20316                 this.tpl.append(this.el, d);
20317             }
20318         }
20319         this.updateIndexes(index);
20320     },
20321
20322     onRemove : function(ds, record, index){
20323        // Roo.log('onRemove');
20324         this.clearSelections();
20325         var el = this.dataName  ?
20326             this.el.child('.roo-tpl-' + this.dataName) :
20327             this.el; 
20328         
20329         el.dom.removeChild(this.nodes[index]);
20330         this.updateIndexes(index);
20331     },
20332
20333     /**
20334      * Refresh an individual node.
20335      * @param {Number} index
20336      */
20337     refreshNode : function(index){
20338         this.onUpdate(this.store, this.store.getAt(index));
20339     },
20340
20341     updateIndexes : function(startIndex, endIndex){
20342         var ns = this.nodes;
20343         startIndex = startIndex || 0;
20344         endIndex = endIndex || ns.length - 1;
20345         for(var i = startIndex; i <= endIndex; i++){
20346             ns[i].nodeIndex = i;
20347         }
20348     },
20349
20350     /**
20351      * Changes the data store this view uses and refresh the view.
20352      * @param {Store} store
20353      */
20354     setStore : function(store, initial){
20355         if(!initial && this.store){
20356             this.store.un("datachanged", this.refresh);
20357             this.store.un("add", this.onAdd);
20358             this.store.un("remove", this.onRemove);
20359             this.store.un("update", this.onUpdate);
20360             this.store.un("clear", this.refresh);
20361             this.store.un("beforeload", this.onBeforeLoad);
20362             this.store.un("load", this.onLoad);
20363             this.store.un("loadexception", this.onLoad);
20364         }
20365         if(store){
20366           
20367             store.on("datachanged", this.refresh, this);
20368             store.on("add", this.onAdd, this);
20369             store.on("remove", this.onRemove, this);
20370             store.on("update", this.onUpdate, this);
20371             store.on("clear", this.refresh, this);
20372             store.on("beforeload", this.onBeforeLoad, this);
20373             store.on("load", this.onLoad, this);
20374             store.on("loadexception", this.onLoad, this);
20375         }
20376         
20377         if(store){
20378             this.refresh();
20379         }
20380     },
20381     /**
20382      * onbeforeLoad - masks the loading area.
20383      *
20384      */
20385     onBeforeLoad : function(store,opts)
20386     {
20387          //Roo.log('onBeforeLoad');   
20388         if (!opts.add) {
20389             this.el.update("");
20390         }
20391         this.el.mask(this.mask ? this.mask : "Loading" ); 
20392     },
20393     onLoad : function ()
20394     {
20395         this.el.unmask();
20396     },
20397     
20398
20399     /**
20400      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20401      * @param {HTMLElement} node
20402      * @return {HTMLElement} The template node
20403      */
20404     findItemFromChild : function(node){
20405         var el = this.dataName  ?
20406             this.el.child('.roo-tpl-' + this.dataName,true) :
20407             this.el.dom; 
20408         
20409         if(!node || node.parentNode == el){
20410                     return node;
20411             }
20412             var p = node.parentNode;
20413             while(p && p != el){
20414             if(p.parentNode == el){
20415                 return p;
20416             }
20417             p = p.parentNode;
20418         }
20419             return null;
20420     },
20421
20422     /** @ignore */
20423     onClick : function(e){
20424         var item = this.findItemFromChild(e.getTarget());
20425         if(item){
20426             var index = this.indexOf(item);
20427             if(this.onItemClick(item, index, e) !== false){
20428                 this.fireEvent("click", this, index, item, e);
20429             }
20430         }else{
20431             this.clearSelections();
20432         }
20433     },
20434
20435     /** @ignore */
20436     onContextMenu : function(e){
20437         var item = this.findItemFromChild(e.getTarget());
20438         if(item){
20439             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20440         }
20441     },
20442
20443     /** @ignore */
20444     onDblClick : function(e){
20445         var item = this.findItemFromChild(e.getTarget());
20446         if(item){
20447             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20448         }
20449     },
20450
20451     onItemClick : function(item, index, e)
20452     {
20453         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20454             return false;
20455         }
20456         if (this.toggleSelect) {
20457             var m = this.isSelected(item) ? 'unselect' : 'select';
20458             //Roo.log(m);
20459             var _t = this;
20460             _t[m](item, true, false);
20461             return true;
20462         }
20463         if(this.multiSelect || this.singleSelect){
20464             if(this.multiSelect && e.shiftKey && this.lastSelection){
20465                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20466             }else{
20467                 this.select(item, this.multiSelect && e.ctrlKey);
20468                 this.lastSelection = item;
20469             }
20470             
20471             if(!this.tickable){
20472                 e.preventDefault();
20473             }
20474             
20475         }
20476         return true;
20477     },
20478
20479     /**
20480      * Get the number of selected nodes.
20481      * @return {Number}
20482      */
20483     getSelectionCount : function(){
20484         return this.selections.length;
20485     },
20486
20487     /**
20488      * Get the currently selected nodes.
20489      * @return {Array} An array of HTMLElements
20490      */
20491     getSelectedNodes : function(){
20492         return this.selections;
20493     },
20494
20495     /**
20496      * Get the indexes of the selected nodes.
20497      * @return {Array}
20498      */
20499     getSelectedIndexes : function(){
20500         var indexes = [], s = this.selections;
20501         for(var i = 0, len = s.length; i < len; i++){
20502             indexes.push(s[i].nodeIndex);
20503         }
20504         return indexes;
20505     },
20506
20507     /**
20508      * Clear all selections
20509      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20510      */
20511     clearSelections : function(suppressEvent){
20512         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20513             this.cmp.elements = this.selections;
20514             this.cmp.removeClass(this.selectedClass);
20515             this.selections = [];
20516             if(!suppressEvent){
20517                 this.fireEvent("selectionchange", this, this.selections);
20518             }
20519         }
20520     },
20521
20522     /**
20523      * Returns true if the passed node is selected
20524      * @param {HTMLElement/Number} node The node or node index
20525      * @return {Boolean}
20526      */
20527     isSelected : function(node){
20528         var s = this.selections;
20529         if(s.length < 1){
20530             return false;
20531         }
20532         node = this.getNode(node);
20533         return s.indexOf(node) !== -1;
20534     },
20535
20536     /**
20537      * Selects nodes.
20538      * @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
20539      * @param {Boolean} keepExisting (optional) true to keep existing selections
20540      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20541      */
20542     select : function(nodeInfo, keepExisting, suppressEvent){
20543         if(nodeInfo instanceof Array){
20544             if(!keepExisting){
20545                 this.clearSelections(true);
20546             }
20547             for(var i = 0, len = nodeInfo.length; i < len; i++){
20548                 this.select(nodeInfo[i], true, true);
20549             }
20550             return;
20551         } 
20552         var node = this.getNode(nodeInfo);
20553         if(!node || this.isSelected(node)){
20554             return; // already selected.
20555         }
20556         if(!keepExisting){
20557             this.clearSelections(true);
20558         }
20559         
20560         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20561             Roo.fly(node).addClass(this.selectedClass);
20562             this.selections.push(node);
20563             if(!suppressEvent){
20564                 this.fireEvent("selectionchange", this, this.selections);
20565             }
20566         }
20567         
20568         
20569     },
20570       /**
20571      * Unselects nodes.
20572      * @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
20573      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20574      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20575      */
20576     unselect : function(nodeInfo, keepExisting, suppressEvent)
20577     {
20578         if(nodeInfo instanceof Array){
20579             Roo.each(this.selections, function(s) {
20580                 this.unselect(s, nodeInfo);
20581             }, this);
20582             return;
20583         }
20584         var node = this.getNode(nodeInfo);
20585         if(!node || !this.isSelected(node)){
20586             //Roo.log("not selected");
20587             return; // not selected.
20588         }
20589         // fireevent???
20590         var ns = [];
20591         Roo.each(this.selections, function(s) {
20592             if (s == node ) {
20593                 Roo.fly(node).removeClass(this.selectedClass);
20594
20595                 return;
20596             }
20597             ns.push(s);
20598         },this);
20599         
20600         this.selections= ns;
20601         this.fireEvent("selectionchange", this, this.selections);
20602     },
20603
20604     /**
20605      * Gets a template node.
20606      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20607      * @return {HTMLElement} The node or null if it wasn't found
20608      */
20609     getNode : function(nodeInfo){
20610         if(typeof nodeInfo == "string"){
20611             return document.getElementById(nodeInfo);
20612         }else if(typeof nodeInfo == "number"){
20613             return this.nodes[nodeInfo];
20614         }
20615         return nodeInfo;
20616     },
20617
20618     /**
20619      * Gets a range template nodes.
20620      * @param {Number} startIndex
20621      * @param {Number} endIndex
20622      * @return {Array} An array of nodes
20623      */
20624     getNodes : function(start, end){
20625         var ns = this.nodes;
20626         start = start || 0;
20627         end = typeof end == "undefined" ? ns.length - 1 : end;
20628         var nodes = [];
20629         if(start <= end){
20630             for(var i = start; i <= end; i++){
20631                 nodes.push(ns[i]);
20632             }
20633         } else{
20634             for(var i = start; i >= end; i--){
20635                 nodes.push(ns[i]);
20636             }
20637         }
20638         return nodes;
20639     },
20640
20641     /**
20642      * Finds the index of the passed node
20643      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20644      * @return {Number} The index of the node or -1
20645      */
20646     indexOf : function(node){
20647         node = this.getNode(node);
20648         if(typeof node.nodeIndex == "number"){
20649             return node.nodeIndex;
20650         }
20651         var ns = this.nodes;
20652         for(var i = 0, len = ns.length; i < len; i++){
20653             if(ns[i] == node){
20654                 return i;
20655             }
20656         }
20657         return -1;
20658     }
20659 });
20660 /*
20661  * - LGPL
20662  *
20663  * based on jquery fullcalendar
20664  * 
20665  */
20666
20667 Roo.bootstrap = Roo.bootstrap || {};
20668 /**
20669  * @class Roo.bootstrap.Calendar
20670  * @extends Roo.bootstrap.Component
20671  * Bootstrap Calendar class
20672  * @cfg {Boolean} loadMask (true|false) default false
20673  * @cfg {Object} header generate the user specific header of the calendar, default false
20674
20675  * @constructor
20676  * Create a new Container
20677  * @param {Object} config The config object
20678  */
20679
20680
20681
20682 Roo.bootstrap.Calendar = function(config){
20683     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20684      this.addEvents({
20685         /**
20686              * @event select
20687              * Fires when a date is selected
20688              * @param {DatePicker} this
20689              * @param {Date} date The selected date
20690              */
20691         'select': true,
20692         /**
20693              * @event monthchange
20694              * Fires when the displayed month changes 
20695              * @param {DatePicker} this
20696              * @param {Date} date The selected month
20697              */
20698         'monthchange': true,
20699         /**
20700              * @event evententer
20701              * Fires when mouse over an event
20702              * @param {Calendar} this
20703              * @param {event} Event
20704              */
20705         'evententer': true,
20706         /**
20707              * @event eventleave
20708              * Fires when the mouse leaves an
20709              * @param {Calendar} this
20710              * @param {event}
20711              */
20712         'eventleave': true,
20713         /**
20714              * @event eventclick
20715              * Fires when the mouse click an
20716              * @param {Calendar} this
20717              * @param {event}
20718              */
20719         'eventclick': true
20720         
20721     });
20722
20723 };
20724
20725 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20726     
20727           /**
20728      * @cfg {Roo.data.Store} store
20729      * The data source for the calendar
20730      */
20731         store : false,
20732      /**
20733      * @cfg {Number} startDay
20734      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20735      */
20736     startDay : 0,
20737     
20738     loadMask : false,
20739     
20740     header : false,
20741       
20742     getAutoCreate : function(){
20743         
20744         
20745         var fc_button = function(name, corner, style, content ) {
20746             return Roo.apply({},{
20747                 tag : 'span',
20748                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20749                          (corner.length ?
20750                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20751                             ''
20752                         ),
20753                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20754                 unselectable: 'on'
20755             });
20756         };
20757         
20758         var header = {};
20759         
20760         if(!this.header){
20761             header = {
20762                 tag : 'table',
20763                 cls : 'fc-header',
20764                 style : 'width:100%',
20765                 cn : [
20766                     {
20767                         tag: 'tr',
20768                         cn : [
20769                             {
20770                                 tag : 'td',
20771                                 cls : 'fc-header-left',
20772                                 cn : [
20773                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20774                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20775                                     { tag: 'span', cls: 'fc-header-space' },
20776                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20777
20778
20779                                 ]
20780                             },
20781
20782                             {
20783                                 tag : 'td',
20784                                 cls : 'fc-header-center',
20785                                 cn : [
20786                                     {
20787                                         tag: 'span',
20788                                         cls: 'fc-header-title',
20789                                         cn : {
20790                                             tag: 'H2',
20791                                             html : 'month / year'
20792                                         }
20793                                     }
20794
20795                                 ]
20796                             },
20797                             {
20798                                 tag : 'td',
20799                                 cls : 'fc-header-right',
20800                                 cn : [
20801                               /*      fc_button('month', 'left', '', 'month' ),
20802                                     fc_button('week', '', '', 'week' ),
20803                                     fc_button('day', 'right', '', 'day' )
20804                                 */    
20805
20806                                 ]
20807                             }
20808
20809                         ]
20810                     }
20811                 ]
20812             };
20813         }
20814         
20815         header = this.header;
20816         
20817        
20818         var cal_heads = function() {
20819             var ret = [];
20820             // fixme - handle this.
20821             
20822             for (var i =0; i < Date.dayNames.length; i++) {
20823                 var d = Date.dayNames[i];
20824                 ret.push({
20825                     tag: 'th',
20826                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20827                     html : d.substring(0,3)
20828                 });
20829                 
20830             }
20831             ret[0].cls += ' fc-first';
20832             ret[6].cls += ' fc-last';
20833             return ret;
20834         };
20835         var cal_cell = function(n) {
20836             return  {
20837                 tag: 'td',
20838                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20839                 cn : [
20840                     {
20841                         cn : [
20842                             {
20843                                 cls: 'fc-day-number',
20844                                 html: 'D'
20845                             },
20846                             {
20847                                 cls: 'fc-day-content',
20848                              
20849                                 cn : [
20850                                      {
20851                                         style: 'position: relative;' // height: 17px;
20852                                     }
20853                                 ]
20854                             }
20855                             
20856                             
20857                         ]
20858                     }
20859                 ]
20860                 
20861             }
20862         };
20863         var cal_rows = function() {
20864             
20865             var ret = [];
20866             for (var r = 0; r < 6; r++) {
20867                 var row= {
20868                     tag : 'tr',
20869                     cls : 'fc-week',
20870                     cn : []
20871                 };
20872                 
20873                 for (var i =0; i < Date.dayNames.length; i++) {
20874                     var d = Date.dayNames[i];
20875                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20876
20877                 }
20878                 row.cn[0].cls+=' fc-first';
20879                 row.cn[0].cn[0].style = 'min-height:90px';
20880                 row.cn[6].cls+=' fc-last';
20881                 ret.push(row);
20882                 
20883             }
20884             ret[0].cls += ' fc-first';
20885             ret[4].cls += ' fc-prev-last';
20886             ret[5].cls += ' fc-last';
20887             return ret;
20888             
20889         };
20890         
20891         var cal_table = {
20892             tag: 'table',
20893             cls: 'fc-border-separate',
20894             style : 'width:100%',
20895             cellspacing  : 0,
20896             cn : [
20897                 { 
20898                     tag: 'thead',
20899                     cn : [
20900                         { 
20901                             tag: 'tr',
20902                             cls : 'fc-first fc-last',
20903                             cn : cal_heads()
20904                         }
20905                     ]
20906                 },
20907                 { 
20908                     tag: 'tbody',
20909                     cn : cal_rows()
20910                 }
20911                   
20912             ]
20913         };
20914          
20915          var cfg = {
20916             cls : 'fc fc-ltr',
20917             cn : [
20918                 header,
20919                 {
20920                     cls : 'fc-content',
20921                     style : "position: relative;",
20922                     cn : [
20923                         {
20924                             cls : 'fc-view fc-view-month fc-grid',
20925                             style : 'position: relative',
20926                             unselectable : 'on',
20927                             cn : [
20928                                 {
20929                                     cls : 'fc-event-container',
20930                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20931                                 },
20932                                 cal_table
20933                             ]
20934                         }
20935                     ]
20936     
20937                 }
20938            ] 
20939             
20940         };
20941         
20942          
20943         
20944         return cfg;
20945     },
20946     
20947     
20948     initEvents : function()
20949     {
20950         if(!this.store){
20951             throw "can not find store for calendar";
20952         }
20953         
20954         var mark = {
20955             tag: "div",
20956             cls:"x-dlg-mask",
20957             style: "text-align:center",
20958             cn: [
20959                 {
20960                     tag: "div",
20961                     style: "background-color:white;width:50%;margin:250 auto",
20962                     cn: [
20963                         {
20964                             tag: "img",
20965                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20966                         },
20967                         {
20968                             tag: "span",
20969                             html: "Loading"
20970                         }
20971                         
20972                     ]
20973                 }
20974             ]
20975         };
20976         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20977         
20978         var size = this.el.select('.fc-content', true).first().getSize();
20979         this.maskEl.setSize(size.width, size.height);
20980         this.maskEl.enableDisplayMode("block");
20981         if(!this.loadMask){
20982             this.maskEl.hide();
20983         }
20984         
20985         this.store = Roo.factory(this.store, Roo.data);
20986         this.store.on('load', this.onLoad, this);
20987         this.store.on('beforeload', this.onBeforeLoad, this);
20988         
20989         this.resize();
20990         
20991         this.cells = this.el.select('.fc-day',true);
20992         //Roo.log(this.cells);
20993         this.textNodes = this.el.query('.fc-day-number');
20994         this.cells.addClassOnOver('fc-state-hover');
20995         
20996         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20997         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20998         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20999         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
21000         
21001         this.on('monthchange', this.onMonthChange, this);
21002         
21003         this.update(new Date().clearTime());
21004     },
21005     
21006     resize : function() {
21007         var sz  = this.el.getSize();
21008         
21009         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
21010         this.el.select('.fc-day-content div',true).setHeight(34);
21011     },
21012     
21013     
21014     // private
21015     showPrevMonth : function(e){
21016         this.update(this.activeDate.add("mo", -1));
21017     },
21018     showToday : function(e){
21019         this.update(new Date().clearTime());
21020     },
21021     // private
21022     showNextMonth : function(e){
21023         this.update(this.activeDate.add("mo", 1));
21024     },
21025
21026     // private
21027     showPrevYear : function(){
21028         this.update(this.activeDate.add("y", -1));
21029     },
21030
21031     // private
21032     showNextYear : function(){
21033         this.update(this.activeDate.add("y", 1));
21034     },
21035
21036     
21037    // private
21038     update : function(date)
21039     {
21040         var vd = this.activeDate;
21041         this.activeDate = date;
21042 //        if(vd && this.el){
21043 //            var t = date.getTime();
21044 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21045 //                Roo.log('using add remove');
21046 //                
21047 //                this.fireEvent('monthchange', this, date);
21048 //                
21049 //                this.cells.removeClass("fc-state-highlight");
21050 //                this.cells.each(function(c){
21051 //                   if(c.dateValue == t){
21052 //                       c.addClass("fc-state-highlight");
21053 //                       setTimeout(function(){
21054 //                            try{c.dom.firstChild.focus();}catch(e){}
21055 //                       }, 50);
21056 //                       return false;
21057 //                   }
21058 //                   return true;
21059 //                });
21060 //                return;
21061 //            }
21062 //        }
21063         
21064         var days = date.getDaysInMonth();
21065         
21066         var firstOfMonth = date.getFirstDateOfMonth();
21067         var startingPos = firstOfMonth.getDay()-this.startDay;
21068         
21069         if(startingPos < this.startDay){
21070             startingPos += 7;
21071         }
21072         
21073         var pm = date.add(Date.MONTH, -1);
21074         var prevStart = pm.getDaysInMonth()-startingPos;
21075 //        
21076         this.cells = this.el.select('.fc-day',true);
21077         this.textNodes = this.el.query('.fc-day-number');
21078         this.cells.addClassOnOver('fc-state-hover');
21079         
21080         var cells = this.cells.elements;
21081         var textEls = this.textNodes;
21082         
21083         Roo.each(cells, function(cell){
21084             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21085         });
21086         
21087         days += startingPos;
21088
21089         // convert everything to numbers so it's fast
21090         var day = 86400000;
21091         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21092         //Roo.log(d);
21093         //Roo.log(pm);
21094         //Roo.log(prevStart);
21095         
21096         var today = new Date().clearTime().getTime();
21097         var sel = date.clearTime().getTime();
21098         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21099         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21100         var ddMatch = this.disabledDatesRE;
21101         var ddText = this.disabledDatesText;
21102         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21103         var ddaysText = this.disabledDaysText;
21104         var format = this.format;
21105         
21106         var setCellClass = function(cal, cell){
21107             cell.row = 0;
21108             cell.events = [];
21109             cell.more = [];
21110             //Roo.log('set Cell Class');
21111             cell.title = "";
21112             var t = d.getTime();
21113             
21114             //Roo.log(d);
21115             
21116             cell.dateValue = t;
21117             if(t == today){
21118                 cell.className += " fc-today";
21119                 cell.className += " fc-state-highlight";
21120                 cell.title = cal.todayText;
21121             }
21122             if(t == sel){
21123                 // disable highlight in other month..
21124                 //cell.className += " fc-state-highlight";
21125                 
21126             }
21127             // disabling
21128             if(t < min) {
21129                 cell.className = " fc-state-disabled";
21130                 cell.title = cal.minText;
21131                 return;
21132             }
21133             if(t > max) {
21134                 cell.className = " fc-state-disabled";
21135                 cell.title = cal.maxText;
21136                 return;
21137             }
21138             if(ddays){
21139                 if(ddays.indexOf(d.getDay()) != -1){
21140                     cell.title = ddaysText;
21141                     cell.className = " fc-state-disabled";
21142                 }
21143             }
21144             if(ddMatch && format){
21145                 var fvalue = d.dateFormat(format);
21146                 if(ddMatch.test(fvalue)){
21147                     cell.title = ddText.replace("%0", fvalue);
21148                     cell.className = " fc-state-disabled";
21149                 }
21150             }
21151             
21152             if (!cell.initialClassName) {
21153                 cell.initialClassName = cell.dom.className;
21154             }
21155             
21156             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21157         };
21158
21159         var i = 0;
21160         
21161         for(; i < startingPos; i++) {
21162             textEls[i].innerHTML = (++prevStart);
21163             d.setDate(d.getDate()+1);
21164             
21165             cells[i].className = "fc-past fc-other-month";
21166             setCellClass(this, cells[i]);
21167         }
21168         
21169         var intDay = 0;
21170         
21171         for(; i < days; i++){
21172             intDay = i - startingPos + 1;
21173             textEls[i].innerHTML = (intDay);
21174             d.setDate(d.getDate()+1);
21175             
21176             cells[i].className = ''; // "x-date-active";
21177             setCellClass(this, cells[i]);
21178         }
21179         var extraDays = 0;
21180         
21181         for(; i < 42; i++) {
21182             textEls[i].innerHTML = (++extraDays);
21183             d.setDate(d.getDate()+1);
21184             
21185             cells[i].className = "fc-future fc-other-month";
21186             setCellClass(this, cells[i]);
21187         }
21188         
21189         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21190         
21191         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21192         
21193         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21194         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21195         
21196         if(totalRows != 6){
21197             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21198             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21199         }
21200         
21201         this.fireEvent('monthchange', this, date);
21202         
21203         
21204         /*
21205         if(!this.internalRender){
21206             var main = this.el.dom.firstChild;
21207             var w = main.offsetWidth;
21208             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21209             Roo.fly(main).setWidth(w);
21210             this.internalRender = true;
21211             // opera does not respect the auto grow header center column
21212             // then, after it gets a width opera refuses to recalculate
21213             // without a second pass
21214             if(Roo.isOpera && !this.secondPass){
21215                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21216                 this.secondPass = true;
21217                 this.update.defer(10, this, [date]);
21218             }
21219         }
21220         */
21221         
21222     },
21223     
21224     findCell : function(dt) {
21225         dt = dt.clearTime().getTime();
21226         var ret = false;
21227         this.cells.each(function(c){
21228             //Roo.log("check " +c.dateValue + '?=' + dt);
21229             if(c.dateValue == dt){
21230                 ret = c;
21231                 return false;
21232             }
21233             return true;
21234         });
21235         
21236         return ret;
21237     },
21238     
21239     findCells : function(ev) {
21240         var s = ev.start.clone().clearTime().getTime();
21241        // Roo.log(s);
21242         var e= ev.end.clone().clearTime().getTime();
21243        // Roo.log(e);
21244         var ret = [];
21245         this.cells.each(function(c){
21246              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21247             
21248             if(c.dateValue > e){
21249                 return ;
21250             }
21251             if(c.dateValue < s){
21252                 return ;
21253             }
21254             ret.push(c);
21255         });
21256         
21257         return ret;    
21258     },
21259     
21260 //    findBestRow: function(cells)
21261 //    {
21262 //        var ret = 0;
21263 //        
21264 //        for (var i =0 ; i < cells.length;i++) {
21265 //            ret  = Math.max(cells[i].rows || 0,ret);
21266 //        }
21267 //        return ret;
21268 //        
21269 //    },
21270     
21271     
21272     addItem : function(ev)
21273     {
21274         // look for vertical location slot in
21275         var cells = this.findCells(ev);
21276         
21277 //        ev.row = this.findBestRow(cells);
21278         
21279         // work out the location.
21280         
21281         var crow = false;
21282         var rows = [];
21283         for(var i =0; i < cells.length; i++) {
21284             
21285             cells[i].row = cells[0].row;
21286             
21287             if(i == 0){
21288                 cells[i].row = cells[i].row + 1;
21289             }
21290             
21291             if (!crow) {
21292                 crow = {
21293                     start : cells[i],
21294                     end :  cells[i]
21295                 };
21296                 continue;
21297             }
21298             if (crow.start.getY() == cells[i].getY()) {
21299                 // on same row.
21300                 crow.end = cells[i];
21301                 continue;
21302             }
21303             // different row.
21304             rows.push(crow);
21305             crow = {
21306                 start: cells[i],
21307                 end : cells[i]
21308             };
21309             
21310         }
21311         
21312         rows.push(crow);
21313         ev.els = [];
21314         ev.rows = rows;
21315         ev.cells = cells;
21316         
21317         cells[0].events.push(ev);
21318         
21319         this.calevents.push(ev);
21320     },
21321     
21322     clearEvents: function() {
21323         
21324         if(!this.calevents){
21325             return;
21326         }
21327         
21328         Roo.each(this.cells.elements, function(c){
21329             c.row = 0;
21330             c.events = [];
21331             c.more = [];
21332         });
21333         
21334         Roo.each(this.calevents, function(e) {
21335             Roo.each(e.els, function(el) {
21336                 el.un('mouseenter' ,this.onEventEnter, this);
21337                 el.un('mouseleave' ,this.onEventLeave, this);
21338                 el.remove();
21339             },this);
21340         },this);
21341         
21342         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21343             e.remove();
21344         });
21345         
21346     },
21347     
21348     renderEvents: function()
21349     {   
21350         var _this = this;
21351         
21352         this.cells.each(function(c) {
21353             
21354             if(c.row < 5){
21355                 return;
21356             }
21357             
21358             var ev = c.events;
21359             
21360             var r = 4;
21361             if(c.row != c.events.length){
21362                 r = 4 - (4 - (c.row - c.events.length));
21363             }
21364             
21365             c.events = ev.slice(0, r);
21366             c.more = ev.slice(r);
21367             
21368             if(c.more.length && c.more.length == 1){
21369                 c.events.push(c.more.pop());
21370             }
21371             
21372             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21373             
21374         });
21375             
21376         this.cells.each(function(c) {
21377             
21378             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21379             
21380             
21381             for (var e = 0; e < c.events.length; e++){
21382                 var ev = c.events[e];
21383                 var rows = ev.rows;
21384                 
21385                 for(var i = 0; i < rows.length; i++) {
21386                 
21387                     // how many rows should it span..
21388
21389                     var  cfg = {
21390                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21391                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21392
21393                         unselectable : "on",
21394                         cn : [
21395                             {
21396                                 cls: 'fc-event-inner',
21397                                 cn : [
21398     //                                {
21399     //                                  tag:'span',
21400     //                                  cls: 'fc-event-time',
21401     //                                  html : cells.length > 1 ? '' : ev.time
21402     //                                },
21403                                     {
21404                                       tag:'span',
21405                                       cls: 'fc-event-title',
21406                                       html : String.format('{0}', ev.title)
21407                                     }
21408
21409
21410                                 ]
21411                             },
21412                             {
21413                                 cls: 'ui-resizable-handle ui-resizable-e',
21414                                 html : '&nbsp;&nbsp;&nbsp'
21415                             }
21416
21417                         ]
21418                     };
21419
21420                     if (i == 0) {
21421                         cfg.cls += ' fc-event-start';
21422                     }
21423                     if ((i+1) == rows.length) {
21424                         cfg.cls += ' fc-event-end';
21425                     }
21426
21427                     var ctr = _this.el.select('.fc-event-container',true).first();
21428                     var cg = ctr.createChild(cfg);
21429
21430                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21431                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21432
21433                     var r = (c.more.length) ? 1 : 0;
21434                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21435                     cg.setWidth(ebox.right - sbox.x -2);
21436
21437                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21438                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21439                     cg.on('click', _this.onEventClick, _this, ev);
21440
21441                     ev.els.push(cg);
21442                     
21443                 }
21444                 
21445             }
21446             
21447             
21448             if(c.more.length){
21449                 var  cfg = {
21450                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21451                     style : 'position: absolute',
21452                     unselectable : "on",
21453                     cn : [
21454                         {
21455                             cls: 'fc-event-inner',
21456                             cn : [
21457                                 {
21458                                   tag:'span',
21459                                   cls: 'fc-event-title',
21460                                   html : 'More'
21461                                 }
21462
21463
21464                             ]
21465                         },
21466                         {
21467                             cls: 'ui-resizable-handle ui-resizable-e',
21468                             html : '&nbsp;&nbsp;&nbsp'
21469                         }
21470
21471                     ]
21472                 };
21473
21474                 var ctr = _this.el.select('.fc-event-container',true).first();
21475                 var cg = ctr.createChild(cfg);
21476
21477                 var sbox = c.select('.fc-day-content',true).first().getBox();
21478                 var ebox = c.select('.fc-day-content',true).first().getBox();
21479                 //Roo.log(cg);
21480                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21481                 cg.setWidth(ebox.right - sbox.x -2);
21482
21483                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21484                 
21485             }
21486             
21487         });
21488         
21489         
21490         
21491     },
21492     
21493     onEventEnter: function (e, el,event,d) {
21494         this.fireEvent('evententer', this, el, event);
21495     },
21496     
21497     onEventLeave: function (e, el,event,d) {
21498         this.fireEvent('eventleave', this, el, event);
21499     },
21500     
21501     onEventClick: function (e, el,event,d) {
21502         this.fireEvent('eventclick', this, el, event);
21503     },
21504     
21505     onMonthChange: function () {
21506         this.store.load();
21507     },
21508     
21509     onMoreEventClick: function(e, el, more)
21510     {
21511         var _this = this;
21512         
21513         this.calpopover.placement = 'right';
21514         this.calpopover.setTitle('More');
21515         
21516         this.calpopover.setContent('');
21517         
21518         var ctr = this.calpopover.el.select('.popover-content', true).first();
21519         
21520         Roo.each(more, function(m){
21521             var cfg = {
21522                 cls : 'fc-event-hori fc-event-draggable',
21523                 html : m.title
21524             };
21525             var cg = ctr.createChild(cfg);
21526             
21527             cg.on('click', _this.onEventClick, _this, m);
21528         });
21529         
21530         this.calpopover.show(el);
21531         
21532         
21533     },
21534     
21535     onLoad: function () 
21536     {   
21537         this.calevents = [];
21538         var cal = this;
21539         
21540         if(this.store.getCount() > 0){
21541             this.store.data.each(function(d){
21542                cal.addItem({
21543                     id : d.data.id,
21544                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21545                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21546                     time : d.data.start_time,
21547                     title : d.data.title,
21548                     description : d.data.description,
21549                     venue : d.data.venue
21550                 });
21551             });
21552         }
21553         
21554         this.renderEvents();
21555         
21556         if(this.calevents.length && this.loadMask){
21557             this.maskEl.hide();
21558         }
21559     },
21560     
21561     onBeforeLoad: function()
21562     {
21563         this.clearEvents();
21564         if(this.loadMask){
21565             this.maskEl.show();
21566         }
21567     }
21568 });
21569
21570  
21571  /*
21572  * - LGPL
21573  *
21574  * element
21575  * 
21576  */
21577
21578 /**
21579  * @class Roo.bootstrap.Popover
21580  * @extends Roo.bootstrap.Component
21581  * @parent none builder
21582  * @children Roo.bootstrap.Component
21583  * Bootstrap Popover class
21584  * @cfg {String} html contents of the popover   (or false to use children..)
21585  * @cfg {String} title of popover (or false to hide)
21586  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21587  * @cfg {String} trigger click || hover (or false to trigger manually)
21588  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21589  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21590  *      - if false and it has a 'parent' then it will be automatically added to that element
21591  *      - if string - Roo.get  will be called 
21592  * @cfg {Number} delay - delay before showing
21593  
21594  * @constructor
21595  * Create a new Popover
21596  * @param {Object} config The config object
21597  */
21598
21599 Roo.bootstrap.Popover = function(config){
21600     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21601     
21602     this.addEvents({
21603         // raw events
21604          /**
21605          * @event show
21606          * After the popover show
21607          * 
21608          * @param {Roo.bootstrap.Popover} this
21609          */
21610         "show" : true,
21611         /**
21612          * @event hide
21613          * After the popover hide
21614          * 
21615          * @param {Roo.bootstrap.Popover} this
21616          */
21617         "hide" : true
21618     });
21619 };
21620
21621 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21622     
21623     title: false,
21624     html: false,
21625     
21626     placement : 'right',
21627     trigger : 'hover', // hover
21628     modal : false,
21629     delay : 0,
21630     
21631     over: false,
21632     
21633     can_build_overlaid : false,
21634     
21635     maskEl : false, // the mask element
21636     headerEl : false,
21637     contentEl : false,
21638     alignEl : false, // when show is called with an element - this get's stored.
21639     
21640     getChildContainer : function()
21641     {
21642         return this.contentEl;
21643         
21644     },
21645     getPopoverHeader : function()
21646     {
21647         this.title = true; // flag not to hide it..
21648         this.headerEl.addClass('p-0');
21649         return this.headerEl
21650     },
21651     
21652     
21653     getAutoCreate : function(){
21654          
21655         var cfg = {
21656            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21657            style: 'display:block',
21658            cn : [
21659                 {
21660                     cls : 'arrow'
21661                 },
21662                 {
21663                     cls : 'popover-inner ',
21664                     cn : [
21665                         {
21666                             tag: 'h3',
21667                             cls: 'popover-title popover-header',
21668                             html : this.title === false ? '' : this.title
21669                         },
21670                         {
21671                             cls : 'popover-content popover-body '  + (this.cls || ''),
21672                             html : this.html || ''
21673                         }
21674                     ]
21675                     
21676                 }
21677            ]
21678         };
21679         
21680         return cfg;
21681     },
21682     /**
21683      * @param {string} the title
21684      */
21685     setTitle: function(str)
21686     {
21687         this.title = str;
21688         if (this.el) {
21689             this.headerEl.dom.innerHTML = str;
21690         }
21691         
21692     },
21693     /**
21694      * @param {string} the body content
21695      */
21696     setContent: function(str)
21697     {
21698         this.html = str;
21699         if (this.contentEl) {
21700             this.contentEl.dom.innerHTML = str;
21701         }
21702         
21703     },
21704     // as it get's added to the bottom of the page.
21705     onRender : function(ct, position)
21706     {
21707         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21708         
21709         
21710         
21711         if(!this.el){
21712             var cfg = Roo.apply({},  this.getAutoCreate());
21713             cfg.id = Roo.id();
21714             
21715             if (this.cls) {
21716                 cfg.cls += ' ' + this.cls;
21717             }
21718             if (this.style) {
21719                 cfg.style = this.style;
21720             }
21721             //Roo.log("adding to ");
21722             this.el = Roo.get(document.body).createChild(cfg, position);
21723 //            Roo.log(this.el);
21724         }
21725         
21726         this.contentEl = this.el.select('.popover-content',true).first();
21727         this.headerEl =  this.el.select('.popover-title',true).first();
21728         
21729         var nitems = [];
21730         if(typeof(this.items) != 'undefined'){
21731             var items = this.items;
21732             delete this.items;
21733
21734             for(var i =0;i < items.length;i++) {
21735                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21736             }
21737         }
21738
21739         this.items = nitems;
21740         
21741         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21742         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21743         
21744         
21745         
21746         this.initEvents();
21747     },
21748     
21749     resizeMask : function()
21750     {
21751         this.maskEl.setSize(
21752             Roo.lib.Dom.getViewWidth(true),
21753             Roo.lib.Dom.getViewHeight(true)
21754         );
21755     },
21756     
21757     initEvents : function()
21758     {
21759         
21760         if (!this.modal) { 
21761             Roo.bootstrap.Popover.register(this);
21762         }
21763          
21764         this.arrowEl = this.el.select('.arrow',true).first();
21765         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21766         this.el.enableDisplayMode('block');
21767         this.el.hide();
21768  
21769         
21770         if (this.over === false && !this.parent()) {
21771             return; 
21772         }
21773         if (this.triggers === false) {
21774             return;
21775         }
21776          
21777         // support parent
21778         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21779         var triggers = this.trigger ? this.trigger.split(' ') : [];
21780         Roo.each(triggers, function(trigger) {
21781         
21782             if (trigger == 'click') {
21783                 on_el.on('click', this.toggle, this);
21784             } else if (trigger != 'manual') {
21785                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21786                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21787       
21788                 on_el.on(eventIn  ,this.enter, this);
21789                 on_el.on(eventOut, this.leave, this);
21790             }
21791         }, this);
21792     },
21793     
21794     
21795     // private
21796     timeout : null,
21797     hoverState : null,
21798     
21799     toggle : function () {
21800         this.hoverState == 'in' ? this.leave() : this.enter();
21801     },
21802     
21803     enter : function () {
21804         
21805         clearTimeout(this.timeout);
21806     
21807         this.hoverState = 'in';
21808     
21809         if (!this.delay || !this.delay.show) {
21810             this.show();
21811             return;
21812         }
21813         var _t = this;
21814         this.timeout = setTimeout(function () {
21815             if (_t.hoverState == 'in') {
21816                 _t.show();
21817             }
21818         }, this.delay.show)
21819     },
21820     
21821     leave : function() {
21822         clearTimeout(this.timeout);
21823     
21824         this.hoverState = 'out';
21825     
21826         if (!this.delay || !this.delay.hide) {
21827             this.hide();
21828             return;
21829         }
21830         var _t = this;
21831         this.timeout = setTimeout(function () {
21832             if (_t.hoverState == 'out') {
21833                 _t.hide();
21834             }
21835         }, this.delay.hide)
21836     },
21837     
21838     /**
21839      * update the position of the dialog
21840      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21841      * 
21842      *
21843      */
21844     
21845     doAlign : function()
21846     {
21847         
21848         if (this.alignEl) {
21849             this.updatePosition(this.placement, true);
21850              
21851         } else {
21852             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21853             var es = this.el.getSize();
21854             var x = Roo.lib.Dom.getViewWidth()/2;
21855             var y = Roo.lib.Dom.getViewHeight()/2;
21856             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21857             
21858         }
21859
21860          
21861          
21862         
21863         
21864     },
21865     
21866     /**
21867      * Show the popover
21868      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21869      * @param {string} (left|right|top|bottom) position
21870      */
21871     show : function (on_el, placement)
21872     {
21873         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21874         on_el = on_el || false; // default to false
21875          
21876         if (!on_el) {
21877             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21878                 on_el = this.parent().el;
21879             } else if (this.over) {
21880                 on_el = Roo.get(this.over);
21881             }
21882             
21883         }
21884         
21885         this.alignEl = Roo.get( on_el );
21886
21887         if (!this.el) {
21888             this.render(document.body);
21889         }
21890         
21891         
21892          
21893         
21894         if (this.title === false) {
21895             this.headerEl.hide();
21896         }
21897         
21898        
21899         this.el.show();
21900         this.el.dom.style.display = 'block';
21901          
21902         this.doAlign();
21903         
21904         //var arrow = this.el.select('.arrow',true).first();
21905         //arrow.set(align[2], 
21906         
21907         this.el.addClass('in');
21908         
21909          
21910         
21911         this.hoverState = 'in';
21912         
21913         if (this.modal) {
21914             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21915             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21916             this.maskEl.dom.style.display = 'block';
21917             this.maskEl.addClass('show');
21918         }
21919         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21920  
21921         this.fireEvent('show', this);
21922         
21923     },
21924     /**
21925      * fire this manually after loading a grid in the table for example
21926      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21927      * @param {Boolean} try and move it if we cant get right position.
21928      */
21929     updatePosition : function(placement, try_move)
21930     {
21931         // allow for calling with no parameters
21932         placement = placement   ? placement :  this.placement;
21933         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21934         
21935         this.el.removeClass([
21936             'fade','top','bottom', 'left', 'right','in',
21937             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21938         ]);
21939         this.el.addClass(placement + ' bs-popover-' + placement);
21940         
21941         if (!this.alignEl ) {
21942             return false;
21943         }
21944         
21945         switch (placement) {
21946             case 'right':
21947                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21948                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21949                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21950                     //normal display... or moved up/down.
21951                     this.el.setXY(offset);
21952                     var xy = this.alignEl.getAnchorXY('tr', false);
21953                     xy[0]+=2;xy[1]+=5;
21954                     this.arrowEl.setXY(xy);
21955                     return true;
21956                 }
21957                 // continue through...
21958                 return this.updatePosition('left', false);
21959                 
21960             
21961             case 'left':
21962                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21963                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21964                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21965                     //normal display... or moved up/down.
21966                     this.el.setXY(offset);
21967                     var xy = this.alignEl.getAnchorXY('tl', false);
21968                     xy[0]-=10;xy[1]+=5; // << fix me
21969                     this.arrowEl.setXY(xy);
21970                     return true;
21971                 }
21972                 // call self...
21973                 return this.updatePosition('right', false);
21974             
21975             case 'top':
21976                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21977                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21978                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21979                     //normal display... or moved up/down.
21980                     this.el.setXY(offset);
21981                     var xy = this.alignEl.getAnchorXY('t', false);
21982                     xy[1]-=10; // << fix me
21983                     this.arrowEl.setXY(xy);
21984                     return true;
21985                 }
21986                 // fall through
21987                return this.updatePosition('bottom', false);
21988             
21989             case 'bottom':
21990                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21991                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21992                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21993                     //normal display... or moved up/down.
21994                     this.el.setXY(offset);
21995                     var xy = this.alignEl.getAnchorXY('b', false);
21996                      xy[1]+=2; // << fix me
21997                     this.arrowEl.setXY(xy);
21998                     return true;
21999                 }
22000                 // fall through
22001                 return this.updatePosition('top', false);
22002                 
22003             
22004         }
22005         
22006         
22007         return false;
22008     },
22009     
22010     hide : function()
22011     {
22012         this.el.setXY([0,0]);
22013         this.el.removeClass('in');
22014         this.el.hide();
22015         this.hoverState = null;
22016         this.maskEl.hide(); // always..
22017         this.fireEvent('hide', this);
22018     }
22019     
22020 });
22021
22022
22023 Roo.apply(Roo.bootstrap.Popover, {
22024
22025     alignment : {
22026         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
22027         'right' : ['l-br', [10,0], 'right bs-popover-right'],
22028         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
22029         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
22030     },
22031     
22032     zIndex : 20001,
22033
22034     clickHander : false,
22035     
22036     
22037
22038     onMouseDown : function(e)
22039     {
22040         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
22041             /// what is nothing is showing..
22042             this.hideAll();
22043         }
22044          
22045     },
22046     
22047     
22048     popups : [],
22049     
22050     register : function(popup)
22051     {
22052         if (!Roo.bootstrap.Popover.clickHandler) {
22053             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22054         }
22055         // hide other popups.
22056         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22057         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22058         this.hideAll(); //<< why?
22059         //this.popups.push(popup);
22060     },
22061     hideAll : function()
22062     {
22063         this.popups.forEach(function(p) {
22064             p.hide();
22065         });
22066     },
22067     onShow : function() {
22068         Roo.bootstrap.Popover.popups.push(this);
22069     },
22070     onHide : function() {
22071         Roo.bootstrap.Popover.popups.remove(this);
22072     } 
22073
22074 });
22075 /**
22076  * @class Roo.bootstrap.PopoverNav
22077  * @extends Roo.bootstrap.nav.Simplebar
22078  * @parent Roo.bootstrap.Popover
22079  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22080  * @licence LGPL
22081  * Bootstrap Popover header navigation class
22082  * FIXME? should this go under nav?
22083  *
22084  * 
22085  * @constructor
22086  * Create a new Popover Header Navigation 
22087  * @param {Object} config The config object
22088  */
22089
22090 Roo.bootstrap.PopoverNav = function(config){
22091     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22092 };
22093
22094 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22095     
22096     
22097     container_method : 'getPopoverHeader' 
22098     
22099      
22100     
22101     
22102    
22103 });
22104
22105  
22106
22107  /*
22108  * - LGPL
22109  *
22110  * Progress
22111  * 
22112  */
22113
22114 /**
22115  * @class Roo.bootstrap.Progress
22116  * @extends Roo.bootstrap.Component
22117  * @children Roo.bootstrap.ProgressBar
22118  * Bootstrap Progress class
22119  * @cfg {Boolean} striped striped of the progress bar
22120  * @cfg {Boolean} active animated of the progress bar
22121  * 
22122  * 
22123  * @constructor
22124  * Create a new Progress
22125  * @param {Object} config The config object
22126  */
22127
22128 Roo.bootstrap.Progress = function(config){
22129     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22130 };
22131
22132 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22133     
22134     striped : false,
22135     active: false,
22136     
22137     getAutoCreate : function(){
22138         var cfg = {
22139             tag: 'div',
22140             cls: 'progress'
22141         };
22142         
22143         
22144         if(this.striped){
22145             cfg.cls += ' progress-striped';
22146         }
22147       
22148         if(this.active){
22149             cfg.cls += ' active';
22150         }
22151         
22152         
22153         return cfg;
22154     }
22155    
22156 });
22157
22158  
22159
22160  /*
22161  * - LGPL
22162  *
22163  * ProgressBar
22164  * 
22165  */
22166
22167 /**
22168  * @class Roo.bootstrap.ProgressBar
22169  * @extends Roo.bootstrap.Component
22170  * Bootstrap ProgressBar class
22171  * @cfg {Number} aria_valuenow aria-value now
22172  * @cfg {Number} aria_valuemin aria-value min
22173  * @cfg {Number} aria_valuemax aria-value max
22174  * @cfg {String} label label for the progress bar
22175  * @cfg {String} panel (success | info | warning | danger )
22176  * @cfg {String} role role of the progress bar
22177  * @cfg {String} sr_only text
22178  * 
22179  * 
22180  * @constructor
22181  * Create a new ProgressBar
22182  * @param {Object} config The config object
22183  */
22184
22185 Roo.bootstrap.ProgressBar = function(config){
22186     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22187 };
22188
22189 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22190     
22191     aria_valuenow : 0,
22192     aria_valuemin : 0,
22193     aria_valuemax : 100,
22194     label : false,
22195     panel : false,
22196     role : false,
22197     sr_only: false,
22198     
22199     getAutoCreate : function()
22200     {
22201         
22202         var cfg = {
22203             tag: 'div',
22204             cls: 'progress-bar',
22205             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22206         };
22207         
22208         if(this.sr_only){
22209             cfg.cn = {
22210                 tag: 'span',
22211                 cls: 'sr-only',
22212                 html: this.sr_only
22213             }
22214         }
22215         
22216         if(this.role){
22217             cfg.role = this.role;
22218         }
22219         
22220         if(this.aria_valuenow){
22221             cfg['aria-valuenow'] = this.aria_valuenow;
22222         }
22223         
22224         if(this.aria_valuemin){
22225             cfg['aria-valuemin'] = this.aria_valuemin;
22226         }
22227         
22228         if(this.aria_valuemax){
22229             cfg['aria-valuemax'] = this.aria_valuemax;
22230         }
22231         
22232         if(this.label && !this.sr_only){
22233             cfg.html = this.label;
22234         }
22235         
22236         if(this.panel){
22237             cfg.cls += ' progress-bar-' + this.panel;
22238         }
22239         
22240         return cfg;
22241     },
22242     
22243     update : function(aria_valuenow)
22244     {
22245         this.aria_valuenow = aria_valuenow;
22246         
22247         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22248     }
22249    
22250 });
22251
22252  
22253
22254  /**
22255  * @class Roo.bootstrap.TabGroup
22256  * @extends Roo.bootstrap.Column
22257  * @children Roo.bootstrap.TabPanel
22258  * Bootstrap Column class
22259  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22260  * @cfg {Boolean} carousel true to make the group behave like a carousel
22261  * @cfg {Boolean} bullets show bullets for the panels
22262  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22263  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22264  * @cfg {Boolean} showarrow (true|false) show arrow default true
22265  * 
22266  * @constructor
22267  * Create a new TabGroup
22268  * @param {Object} config The config object
22269  */
22270
22271 Roo.bootstrap.TabGroup = function(config){
22272     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22273     if (!this.navId) {
22274         this.navId = Roo.id();
22275     }
22276     this.tabs = [];
22277     Roo.bootstrap.TabGroup.register(this);
22278     
22279 };
22280
22281 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22282     
22283     carousel : false,
22284     transition : false,
22285     bullets : 0,
22286     timer : 0,
22287     autoslide : false,
22288     slideFn : false,
22289     slideOnTouch : false,
22290     showarrow : true,
22291     
22292     getAutoCreate : function()
22293     {
22294         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22295         
22296         cfg.cls += ' tab-content';
22297         
22298         if (this.carousel) {
22299             cfg.cls += ' carousel slide';
22300             
22301             cfg.cn = [{
22302                cls : 'carousel-inner',
22303                cn : []
22304             }];
22305         
22306             if(this.bullets  && !Roo.isTouch){
22307                 
22308                 var bullets = {
22309                     cls : 'carousel-bullets',
22310                     cn : []
22311                 };
22312                
22313                 if(this.bullets_cls){
22314                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22315                 }
22316                 
22317                 bullets.cn.push({
22318                     cls : 'clear'
22319                 });
22320                 
22321                 cfg.cn[0].cn.push(bullets);
22322             }
22323             
22324             if(this.showarrow){
22325                 cfg.cn[0].cn.push({
22326                     tag : 'div',
22327                     class : 'carousel-arrow',
22328                     cn : [
22329                         {
22330                             tag : 'div',
22331                             class : 'carousel-prev',
22332                             cn : [
22333                                 {
22334                                     tag : 'i',
22335                                     class : 'fa fa-chevron-left'
22336                                 }
22337                             ]
22338                         },
22339                         {
22340                             tag : 'div',
22341                             class : 'carousel-next',
22342                             cn : [
22343                                 {
22344                                     tag : 'i',
22345                                     class : 'fa fa-chevron-right'
22346                                 }
22347                             ]
22348                         }
22349                     ]
22350                 });
22351             }
22352             
22353         }
22354         
22355         return cfg;
22356     },
22357     
22358     initEvents:  function()
22359     {
22360 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22361 //            this.el.on("touchstart", this.onTouchStart, this);
22362 //        }
22363         
22364         if(this.autoslide){
22365             var _this = this;
22366             
22367             this.slideFn = window.setInterval(function() {
22368                 _this.showPanelNext();
22369             }, this.timer);
22370         }
22371         
22372         if(this.showarrow){
22373             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22374             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22375         }
22376         
22377         
22378     },
22379     
22380 //    onTouchStart : function(e, el, o)
22381 //    {
22382 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22383 //            return;
22384 //        }
22385 //        
22386 //        this.showPanelNext();
22387 //    },
22388     
22389     
22390     getChildContainer : function()
22391     {
22392         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22393     },
22394     
22395     /**
22396     * register a Navigation item
22397     * @param {Roo.bootstrap.nav.Item} the navitem to add
22398     */
22399     register : function(item)
22400     {
22401         this.tabs.push( item);
22402         item.navId = this.navId; // not really needed..
22403         this.addBullet();
22404     
22405     },
22406     
22407     getActivePanel : function()
22408     {
22409         var r = false;
22410         Roo.each(this.tabs, function(t) {
22411             if (t.active) {
22412                 r = t;
22413                 return false;
22414             }
22415             return null;
22416         });
22417         return r;
22418         
22419     },
22420     getPanelByName : function(n)
22421     {
22422         var r = false;
22423         Roo.each(this.tabs, function(t) {
22424             if (t.tabId == n) {
22425                 r = t;
22426                 return false;
22427             }
22428             return null;
22429         });
22430         return r;
22431     },
22432     indexOfPanel : function(p)
22433     {
22434         var r = false;
22435         Roo.each(this.tabs, function(t,i) {
22436             if (t.tabId == p.tabId) {
22437                 r = i;
22438                 return false;
22439             }
22440             return null;
22441         });
22442         return r;
22443     },
22444     /**
22445      * show a specific panel
22446      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22447      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22448      */
22449     showPanel : function (pan)
22450     {
22451         if(this.transition || typeof(pan) == 'undefined'){
22452             Roo.log("waiting for the transitionend");
22453             return false;
22454         }
22455         
22456         if (typeof(pan) == 'number') {
22457             pan = this.tabs[pan];
22458         }
22459         
22460         if (typeof(pan) == 'string') {
22461             pan = this.getPanelByName(pan);
22462         }
22463         
22464         var cur = this.getActivePanel();
22465         
22466         if(!pan || !cur){
22467             Roo.log('pan or acitve pan is undefined');
22468             return false;
22469         }
22470         
22471         if (pan.tabId == this.getActivePanel().tabId) {
22472             return true;
22473         }
22474         
22475         if (false === cur.fireEvent('beforedeactivate')) {
22476             return false;
22477         }
22478         
22479         if(this.bullets > 0 && !Roo.isTouch){
22480             this.setActiveBullet(this.indexOfPanel(pan));
22481         }
22482         
22483         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22484             
22485             //class="carousel-item carousel-item-next carousel-item-left"
22486             
22487             this.transition = true;
22488             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22489             var lr = dir == 'next' ? 'left' : 'right';
22490             pan.el.addClass(dir); // or prev
22491             pan.el.addClass('carousel-item-' + dir); // or prev
22492             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22493             cur.el.addClass(lr); // or right
22494             pan.el.addClass(lr);
22495             cur.el.addClass('carousel-item-' +lr); // or right
22496             pan.el.addClass('carousel-item-' +lr);
22497             
22498             
22499             var _this = this;
22500             cur.el.on('transitionend', function() {
22501                 Roo.log("trans end?");
22502                 
22503                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22504                 pan.setActive(true);
22505                 
22506                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22507                 cur.setActive(false);
22508                 
22509                 _this.transition = false;
22510                 
22511             }, this, { single:  true } );
22512             
22513             return true;
22514         }
22515         
22516         cur.setActive(false);
22517         pan.setActive(true);
22518         
22519         return true;
22520         
22521     },
22522     showPanelNext : function()
22523     {
22524         var i = this.indexOfPanel(this.getActivePanel());
22525         
22526         if (i >= this.tabs.length - 1 && !this.autoslide) {
22527             return;
22528         }
22529         
22530         if (i >= this.tabs.length - 1 && this.autoslide) {
22531             i = -1;
22532         }
22533         
22534         this.showPanel(this.tabs[i+1]);
22535     },
22536     
22537     showPanelPrev : function()
22538     {
22539         var i = this.indexOfPanel(this.getActivePanel());
22540         
22541         if (i  < 1 && !this.autoslide) {
22542             return;
22543         }
22544         
22545         if (i < 1 && this.autoslide) {
22546             i = this.tabs.length;
22547         }
22548         
22549         this.showPanel(this.tabs[i-1]);
22550     },
22551     
22552     
22553     addBullet: function()
22554     {
22555         if(!this.bullets || Roo.isTouch){
22556             return;
22557         }
22558         var ctr = this.el.select('.carousel-bullets',true).first();
22559         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22560         var bullet = ctr.createChild({
22561             cls : 'bullet bullet-' + i
22562         },ctr.dom.lastChild);
22563         
22564         
22565         var _this = this;
22566         
22567         bullet.on('click', (function(e, el, o, ii, t){
22568
22569             e.preventDefault();
22570
22571             this.showPanel(ii);
22572
22573             if(this.autoslide && this.slideFn){
22574                 clearInterval(this.slideFn);
22575                 this.slideFn = window.setInterval(function() {
22576                     _this.showPanelNext();
22577                 }, this.timer);
22578             }
22579
22580         }).createDelegate(this, [i, bullet], true));
22581                 
22582         
22583     },
22584      
22585     setActiveBullet : function(i)
22586     {
22587         if(Roo.isTouch){
22588             return;
22589         }
22590         
22591         Roo.each(this.el.select('.bullet', true).elements, function(el){
22592             el.removeClass('selected');
22593         });
22594
22595         var bullet = this.el.select('.bullet-' + i, true).first();
22596         
22597         if(!bullet){
22598             return;
22599         }
22600         
22601         bullet.addClass('selected');
22602     }
22603     
22604     
22605   
22606 });
22607
22608  
22609
22610  
22611  
22612 Roo.apply(Roo.bootstrap.TabGroup, {
22613     
22614     groups: {},
22615      /**
22616     * register a Navigation Group
22617     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22618     */
22619     register : function(navgrp)
22620     {
22621         this.groups[navgrp.navId] = navgrp;
22622         
22623     },
22624     /**
22625     * fetch a Navigation Group based on the navigation ID
22626     * if one does not exist , it will get created.
22627     * @param {string} the navgroup to add
22628     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22629     */
22630     get: function(navId) {
22631         if (typeof(this.groups[navId]) == 'undefined') {
22632             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22633         }
22634         return this.groups[navId] ;
22635     }
22636     
22637     
22638     
22639 });
22640
22641  /*
22642  * - LGPL
22643  *
22644  * TabPanel
22645  * 
22646  */
22647
22648 /**
22649  * @class Roo.bootstrap.TabPanel
22650  * @extends Roo.bootstrap.Component
22651  * @children Roo.bootstrap.Component
22652  * Bootstrap TabPanel class
22653  * @cfg {Boolean} active panel active
22654  * @cfg {String} html panel content
22655  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22656  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22657  * @cfg {String} href click to link..
22658  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22659  * 
22660  * 
22661  * @constructor
22662  * Create a new TabPanel
22663  * @param {Object} config The config object
22664  */
22665
22666 Roo.bootstrap.TabPanel = function(config){
22667     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22668     this.addEvents({
22669         /**
22670              * @event changed
22671              * Fires when the active status changes
22672              * @param {Roo.bootstrap.TabPanel} this
22673              * @param {Boolean} state the new state
22674             
22675          */
22676         'changed': true,
22677         /**
22678              * @event beforedeactivate
22679              * Fires before a tab is de-activated - can be used to do validation on a form.
22680              * @param {Roo.bootstrap.TabPanel} this
22681              * @return {Boolean} false if there is an error
22682             
22683          */
22684         'beforedeactivate': true
22685      });
22686     
22687     this.tabId = this.tabId || Roo.id();
22688   
22689 };
22690
22691 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22692     
22693     active: false,
22694     html: false,
22695     tabId: false,
22696     navId : false,
22697     href : '',
22698     touchSlide : false,
22699     getAutoCreate : function(){
22700         
22701         
22702         var cfg = {
22703             tag: 'div',
22704             // item is needed for carousel - not sure if it has any effect otherwise
22705             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22706             html: this.html || ''
22707         };
22708         
22709         if(this.active){
22710             cfg.cls += ' active';
22711         }
22712         
22713         if(this.tabId){
22714             cfg.tabId = this.tabId;
22715         }
22716         
22717         
22718         
22719         return cfg;
22720     },
22721     
22722     initEvents:  function()
22723     {
22724         var p = this.parent();
22725         
22726         this.navId = this.navId || p.navId;
22727         
22728         if (typeof(this.navId) != 'undefined') {
22729             // not really needed.. but just in case.. parent should be a NavGroup.
22730             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22731             
22732             tg.register(this);
22733             
22734             var i = tg.tabs.length - 1;
22735             
22736             if(this.active && tg.bullets > 0 && i < tg.bullets){
22737                 tg.setActiveBullet(i);
22738             }
22739         }
22740         
22741         this.el.on('click', this.onClick, this);
22742         
22743         if(Roo.isTouch && this.touchSlide){
22744             this.el.on("touchstart", this.onTouchStart, this);
22745             this.el.on("touchmove", this.onTouchMove, this);
22746             this.el.on("touchend", this.onTouchEnd, this);
22747         }
22748         
22749     },
22750     
22751     onRender : function(ct, position)
22752     {
22753         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22754     },
22755     
22756     setActive : function(state)
22757     {
22758         Roo.log("panel - set active " + this.tabId + "=" + state);
22759         
22760         this.active = state;
22761         if (!state) {
22762             this.el.removeClass('active');
22763             
22764         } else  if (!this.el.hasClass('active')) {
22765             this.el.addClass('active');
22766         }
22767         
22768         this.fireEvent('changed', this, state);
22769     },
22770     
22771     onClick : function(e)
22772     {
22773         e.preventDefault();
22774         
22775         if(!this.href.length){
22776             return;
22777         }
22778         
22779         window.location.href = this.href;
22780     },
22781     
22782     startX : 0,
22783     startY : 0,
22784     endX : 0,
22785     endY : 0,
22786     swiping : false,
22787     
22788     onTouchStart : function(e)
22789     {
22790         this.swiping = false;
22791         
22792         this.startX = e.browserEvent.touches[0].clientX;
22793         this.startY = e.browserEvent.touches[0].clientY;
22794     },
22795     
22796     onTouchMove : function(e)
22797     {
22798         this.swiping = true;
22799         
22800         this.endX = e.browserEvent.touches[0].clientX;
22801         this.endY = e.browserEvent.touches[0].clientY;
22802     },
22803     
22804     onTouchEnd : function(e)
22805     {
22806         if(!this.swiping){
22807             this.onClick(e);
22808             return;
22809         }
22810         
22811         var tabGroup = this.parent();
22812         
22813         if(this.endX > this.startX){ // swiping right
22814             tabGroup.showPanelPrev();
22815             return;
22816         }
22817         
22818         if(this.startX > this.endX){ // swiping left
22819             tabGroup.showPanelNext();
22820             return;
22821         }
22822     }
22823     
22824     
22825 });
22826  
22827
22828  
22829
22830  /*
22831  * - LGPL
22832  *
22833  * DateField
22834  * 
22835  */
22836
22837 /**
22838  * @class Roo.bootstrap.form.DateField
22839  * @extends Roo.bootstrap.form.Input
22840  * Bootstrap DateField class
22841  * @cfg {Number} weekStart default 0
22842  * @cfg {String} viewMode default empty, (months|years)
22843  * @cfg {String} minViewMode default empty, (months|years)
22844  * @cfg {Number} startDate default -Infinity
22845  * @cfg {Number} endDate default Infinity
22846  * @cfg {Boolean} todayHighlight default false
22847  * @cfg {Boolean} todayBtn default false
22848  * @cfg {Boolean} calendarWeeks default false
22849  * @cfg {Object} daysOfWeekDisabled default empty
22850  * @cfg {Boolean} singleMode default false (true | false)
22851  * 
22852  * @cfg {Boolean} keyboardNavigation default true
22853  * @cfg {String} language default en
22854  * 
22855  * @constructor
22856  * Create a new DateField
22857  * @param {Object} config The config object
22858  */
22859
22860 Roo.bootstrap.form.DateField = function(config){
22861     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22862      this.addEvents({
22863             /**
22864              * @event show
22865              * Fires when this field show.
22866              * @param {Roo.bootstrap.form.DateField} this
22867              * @param {Mixed} date The date value
22868              */
22869             show : true,
22870             /**
22871              * @event show
22872              * Fires when this field hide.
22873              * @param {Roo.bootstrap.form.DateField} this
22874              * @param {Mixed} date The date value
22875              */
22876             hide : true,
22877             /**
22878              * @event select
22879              * Fires when select a date.
22880              * @param {Roo.bootstrap.form.DateField} this
22881              * @param {Mixed} date The date value
22882              */
22883             select : true,
22884             /**
22885              * @event beforeselect
22886              * Fires when before select a date.
22887              * @param {Roo.bootstrap.form.DateField} this
22888              * @param {Mixed} date The date value
22889              */
22890             beforeselect : true
22891         });
22892 };
22893
22894 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22895     
22896     /**
22897      * @cfg {String} format
22898      * The default date format string which can be overriden for localization support.  The format must be
22899      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22900      */
22901     format : "m/d/y",
22902     /**
22903      * @cfg {String} altFormats
22904      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22905      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22906      */
22907     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22908     
22909     weekStart : 0,
22910     
22911     viewMode : '',
22912     
22913     minViewMode : '',
22914     
22915     todayHighlight : false,
22916     
22917     todayBtn: false,
22918     
22919     language: 'en',
22920     
22921     keyboardNavigation: true,
22922     
22923     calendarWeeks: false,
22924     
22925     startDate: -Infinity,
22926     
22927     endDate: Infinity,
22928     
22929     daysOfWeekDisabled: [],
22930     
22931     _events: [],
22932     
22933     singleMode : false,
22934     
22935     UTCDate: function()
22936     {
22937         return new Date(Date.UTC.apply(Date, arguments));
22938     },
22939     
22940     UTCToday: function()
22941     {
22942         var today = new Date();
22943         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22944     },
22945     
22946     getDate: function() {
22947             var d = this.getUTCDate();
22948             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22949     },
22950     
22951     getUTCDate: function() {
22952             return this.date;
22953     },
22954     
22955     setDate: function(d) {
22956             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22957     },
22958     
22959     setUTCDate: function(d) {
22960             this.date = d;
22961             this.setValue(this.formatDate(this.date));
22962     },
22963         
22964     onRender: function(ct, position)
22965     {
22966         
22967         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22968         
22969         this.language = this.language || 'en';
22970         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22971         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22972         
22973         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22974         this.format = this.format || 'm/d/y';
22975         this.isInline = false;
22976         this.isInput = true;
22977         this.component = this.el.select('.add-on', true).first() || false;
22978         this.component = (this.component && this.component.length === 0) ? false : this.component;
22979         this.hasInput = this.component && this.inputEl().length;
22980         
22981         if (typeof(this.minViewMode === 'string')) {
22982             switch (this.minViewMode) {
22983                 case 'months':
22984                     this.minViewMode = 1;
22985                     break;
22986                 case 'years':
22987                     this.minViewMode = 2;
22988                     break;
22989                 default:
22990                     this.minViewMode = 0;
22991                     break;
22992             }
22993         }
22994         
22995         if (typeof(this.viewMode === 'string')) {
22996             switch (this.viewMode) {
22997                 case 'months':
22998                     this.viewMode = 1;
22999                     break;
23000                 case 'years':
23001                     this.viewMode = 2;
23002                     break;
23003                 default:
23004                     this.viewMode = 0;
23005                     break;
23006             }
23007         }
23008                 
23009         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
23010         
23011 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
23012         
23013         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23014         
23015         this.picker().on('mousedown', this.onMousedown, this);
23016         this.picker().on('click', this.onClick, this);
23017         
23018         this.picker().addClass('datepicker-dropdown');
23019         
23020         this.startViewMode = this.viewMode;
23021         
23022         if(this.singleMode){
23023             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
23024                 v.setVisibilityMode(Roo.Element.DISPLAY);
23025                 v.hide();
23026             });
23027             
23028             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23029                 v.setStyle('width', '189px');
23030             });
23031         }
23032         
23033         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
23034             if(!this.calendarWeeks){
23035                 v.remove();
23036                 return;
23037             }
23038             
23039             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23040             v.attr('colspan', function(i, val){
23041                 return parseInt(val) + 1;
23042             });
23043         });
23044                         
23045         
23046         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23047         
23048         this.setStartDate(this.startDate);
23049         this.setEndDate(this.endDate);
23050         
23051         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23052         
23053         this.fillDow();
23054         this.fillMonths();
23055         this.update();
23056         this.showMode();
23057         
23058         if(this.isInline) {
23059             this.showPopup();
23060         }
23061     },
23062     
23063     picker : function()
23064     {
23065         return this.pickerEl;
23066 //        return this.el.select('.datepicker', true).first();
23067     },
23068     
23069     fillDow: function()
23070     {
23071         var dowCnt = this.weekStart;
23072         
23073         var dow = {
23074             tag: 'tr',
23075             cn: [
23076                 
23077             ]
23078         };
23079         
23080         if(this.calendarWeeks){
23081             dow.cn.push({
23082                 tag: 'th',
23083                 cls: 'cw',
23084                 html: '&nbsp;'
23085             })
23086         }
23087         
23088         while (dowCnt < this.weekStart + 7) {
23089             dow.cn.push({
23090                 tag: 'th',
23091                 cls: 'dow',
23092                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23093             });
23094         }
23095         
23096         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23097     },
23098     
23099     fillMonths: function()
23100     {    
23101         var i = 0;
23102         var months = this.picker().select('>.datepicker-months td', true).first();
23103         
23104         months.dom.innerHTML = '';
23105         
23106         while (i < 12) {
23107             var month = {
23108                 tag: 'span',
23109                 cls: 'month',
23110                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23111             };
23112             
23113             months.createChild(month);
23114         }
23115         
23116     },
23117     
23118     update: function()
23119     {
23120         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;
23121         
23122         if (this.date < this.startDate) {
23123             this.viewDate = new Date(this.startDate);
23124         } else if (this.date > this.endDate) {
23125             this.viewDate = new Date(this.endDate);
23126         } else {
23127             this.viewDate = new Date(this.date);
23128         }
23129         
23130         this.fill();
23131     },
23132     
23133     fill: function() 
23134     {
23135         var d = new Date(this.viewDate),
23136                 year = d.getUTCFullYear(),
23137                 month = d.getUTCMonth(),
23138                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23139                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23140                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23141                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23142                 currentDate = this.date && this.date.valueOf(),
23143                 today = this.UTCToday();
23144         
23145         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23146         
23147 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23148         
23149 //        this.picker.select('>tfoot th.today').
23150 //                                              .text(dates[this.language].today)
23151 //                                              .toggle(this.todayBtn !== false);
23152     
23153         this.updateNavArrows();
23154         this.fillMonths();
23155                                                 
23156         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23157         
23158         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23159          
23160         prevMonth.setUTCDate(day);
23161         
23162         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23163         
23164         var nextMonth = new Date(prevMonth);
23165         
23166         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23167         
23168         nextMonth = nextMonth.valueOf();
23169         
23170         var fillMonths = false;
23171         
23172         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23173         
23174         while(prevMonth.valueOf() <= nextMonth) {
23175             var clsName = '';
23176             
23177             if (prevMonth.getUTCDay() === this.weekStart) {
23178                 if(fillMonths){
23179                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23180                 }
23181                     
23182                 fillMonths = {
23183                     tag: 'tr',
23184                     cn: []
23185                 };
23186                 
23187                 if(this.calendarWeeks){
23188                     // ISO 8601: First week contains first thursday.
23189                     // ISO also states week starts on Monday, but we can be more abstract here.
23190                     var
23191                     // Start of current week: based on weekstart/current date
23192                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23193                     // Thursday of this week
23194                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23195                     // First Thursday of year, year from thursday
23196                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23197                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23198                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23199                     
23200                     fillMonths.cn.push({
23201                         tag: 'td',
23202                         cls: 'cw',
23203                         html: calWeek
23204                     });
23205                 }
23206             }
23207             
23208             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23209                 clsName += ' old';
23210             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23211                 clsName += ' new';
23212             }
23213             if (this.todayHighlight &&
23214                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23215                 prevMonth.getUTCMonth() == today.getMonth() &&
23216                 prevMonth.getUTCDate() == today.getDate()) {
23217                 clsName += ' today';
23218             }
23219             
23220             if (currentDate && prevMonth.valueOf() === currentDate) {
23221                 clsName += ' active';
23222             }
23223             
23224             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23225                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23226                     clsName += ' disabled';
23227             }
23228             
23229             fillMonths.cn.push({
23230                 tag: 'td',
23231                 cls: 'day ' + clsName,
23232                 html: prevMonth.getDate()
23233             });
23234             
23235             prevMonth.setDate(prevMonth.getDate()+1);
23236         }
23237           
23238         var currentYear = this.date && this.date.getUTCFullYear();
23239         var currentMonth = this.date && this.date.getUTCMonth();
23240         
23241         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23242         
23243         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23244             v.removeClass('active');
23245             
23246             if(currentYear === year && k === currentMonth){
23247                 v.addClass('active');
23248             }
23249             
23250             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23251                 v.addClass('disabled');
23252             }
23253             
23254         });
23255         
23256         
23257         year = parseInt(year/10, 10) * 10;
23258         
23259         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23260         
23261         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23262         
23263         year -= 1;
23264         for (var i = -1; i < 11; i++) {
23265             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23266                 tag: 'span',
23267                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23268                 html: year
23269             });
23270             
23271             year += 1;
23272         }
23273     },
23274     
23275     showMode: function(dir) 
23276     {
23277         if (dir) {
23278             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23279         }
23280         
23281         Roo.each(this.picker().select('>div',true).elements, function(v){
23282             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23283             v.hide();
23284         });
23285         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23286     },
23287     
23288     place: function()
23289     {
23290         if(this.isInline) {
23291             return;
23292         }
23293         
23294         this.picker().removeClass(['bottom', 'top']);
23295         
23296         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23297             /*
23298              * place to the top of element!
23299              *
23300              */
23301             
23302             this.picker().addClass('top');
23303             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23304             
23305             return;
23306         }
23307         
23308         this.picker().addClass('bottom');
23309         
23310         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23311     },
23312     
23313     parseDate : function(value)
23314     {
23315         if(!value || value instanceof Date){
23316             return value;
23317         }
23318         var v = Date.parseDate(value, this.format);
23319         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23320             v = Date.parseDate(value, 'Y-m-d');
23321         }
23322         if(!v && this.altFormats){
23323             if(!this.altFormatsArray){
23324                 this.altFormatsArray = this.altFormats.split("|");
23325             }
23326             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23327                 v = Date.parseDate(value, this.altFormatsArray[i]);
23328             }
23329         }
23330         return v;
23331     },
23332     
23333     formatDate : function(date, fmt)
23334     {   
23335         return (!date || !(date instanceof Date)) ?
23336         date : date.dateFormat(fmt || this.format);
23337     },
23338     
23339     onFocus : function()
23340     {
23341         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23342         this.showPopup();
23343     },
23344     
23345     onBlur : function()
23346     {
23347         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23348         
23349         var d = this.inputEl().getValue();
23350         
23351         this.setValue(d);
23352                 
23353         this.hidePopup();
23354     },
23355     
23356     showPopup : function()
23357     {
23358         this.picker().show();
23359         this.update();
23360         this.place();
23361         
23362         this.fireEvent('showpopup', this, this.date);
23363     },
23364     
23365     hidePopup : function()
23366     {
23367         if(this.isInline) {
23368             return;
23369         }
23370         this.picker().hide();
23371         this.viewMode = this.startViewMode;
23372         this.showMode();
23373         
23374         this.fireEvent('hidepopup', this, this.date);
23375         
23376     },
23377     
23378     onMousedown: function(e)
23379     {
23380         e.stopPropagation();
23381         e.preventDefault();
23382     },
23383     
23384     keyup: function(e)
23385     {
23386         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23387         this.update();
23388     },
23389
23390     setValue: function(v)
23391     {
23392         if(this.fireEvent('beforeselect', this, v) !== false){
23393             var d = new Date(this.parseDate(v) ).clearTime();
23394         
23395             if(isNaN(d.getTime())){
23396                 this.date = this.viewDate = '';
23397                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23398                 return;
23399             }
23400
23401             v = this.formatDate(d);
23402
23403             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23404
23405             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23406
23407             this.update();
23408
23409             this.fireEvent('select', this, this.date);
23410         }
23411     },
23412     
23413     getValue: function()
23414     {
23415         return this.formatDate(this.date);
23416     },
23417     
23418     fireKey: function(e)
23419     {
23420         if (!this.picker().isVisible()){
23421             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23422                 this.showPopup();
23423             }
23424             return;
23425         }
23426         
23427         var dateChanged = false,
23428         dir, day, month,
23429         newDate, newViewDate;
23430         
23431         switch(e.keyCode){
23432             case 27: // escape
23433                 this.hidePopup();
23434                 e.preventDefault();
23435                 break;
23436             case 37: // left
23437             case 39: // right
23438                 if (!this.keyboardNavigation) {
23439                     break;
23440                 }
23441                 dir = e.keyCode == 37 ? -1 : 1;
23442                 
23443                 if (e.ctrlKey){
23444                     newDate = this.moveYear(this.date, dir);
23445                     newViewDate = this.moveYear(this.viewDate, dir);
23446                 } else if (e.shiftKey){
23447                     newDate = this.moveMonth(this.date, dir);
23448                     newViewDate = this.moveMonth(this.viewDate, dir);
23449                 } else {
23450                     newDate = new Date(this.date);
23451                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23452                     newViewDate = new Date(this.viewDate);
23453                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23454                 }
23455                 if (this.dateWithinRange(newDate)){
23456                     this.date = newDate;
23457                     this.viewDate = newViewDate;
23458                     this.setValue(this.formatDate(this.date));
23459 //                    this.update();
23460                     e.preventDefault();
23461                     dateChanged = true;
23462                 }
23463                 break;
23464             case 38: // up
23465             case 40: // down
23466                 if (!this.keyboardNavigation) {
23467                     break;
23468                 }
23469                 dir = e.keyCode == 38 ? -1 : 1;
23470                 if (e.ctrlKey){
23471                     newDate = this.moveYear(this.date, dir);
23472                     newViewDate = this.moveYear(this.viewDate, dir);
23473                 } else if (e.shiftKey){
23474                     newDate = this.moveMonth(this.date, dir);
23475                     newViewDate = this.moveMonth(this.viewDate, dir);
23476                 } else {
23477                     newDate = new Date(this.date);
23478                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23479                     newViewDate = new Date(this.viewDate);
23480                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23481                 }
23482                 if (this.dateWithinRange(newDate)){
23483                     this.date = newDate;
23484                     this.viewDate = newViewDate;
23485                     this.setValue(this.formatDate(this.date));
23486 //                    this.update();
23487                     e.preventDefault();
23488                     dateChanged = true;
23489                 }
23490                 break;
23491             case 13: // enter
23492                 this.setValue(this.formatDate(this.date));
23493                 this.hidePopup();
23494                 e.preventDefault();
23495                 break;
23496             case 9: // tab
23497                 this.setValue(this.formatDate(this.date));
23498                 this.hidePopup();
23499                 break;
23500             case 16: // shift
23501             case 17: // ctrl
23502             case 18: // alt
23503                 break;
23504             default :
23505                 this.hidePopup();
23506                 
23507         }
23508     },
23509     
23510     
23511     onClick: function(e) 
23512     {
23513         e.stopPropagation();
23514         e.preventDefault();
23515         
23516         var target = e.getTarget();
23517         
23518         if(target.nodeName.toLowerCase() === 'i'){
23519             target = Roo.get(target).dom.parentNode;
23520         }
23521         
23522         var nodeName = target.nodeName;
23523         var className = target.className;
23524         var html = target.innerHTML;
23525         //Roo.log(nodeName);
23526         
23527         switch(nodeName.toLowerCase()) {
23528             case 'th':
23529                 switch(className) {
23530                     case 'switch':
23531                         this.showMode(1);
23532                         break;
23533                     case 'prev':
23534                     case 'next':
23535                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23536                         switch(this.viewMode){
23537                                 case 0:
23538                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23539                                         break;
23540                                 case 1:
23541                                 case 2:
23542                                         this.viewDate = this.moveYear(this.viewDate, dir);
23543                                         break;
23544                         }
23545                         this.fill();
23546                         break;
23547                     case 'today':
23548                         var date = new Date();
23549                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23550 //                        this.fill()
23551                         this.setValue(this.formatDate(this.date));
23552                         
23553                         this.hidePopup();
23554                         break;
23555                 }
23556                 break;
23557             case 'span':
23558                 if (className.indexOf('disabled') < 0) {
23559                 if (!this.viewDate) {
23560                     this.viewDate = new Date();
23561                 }
23562                 this.viewDate.setUTCDate(1);
23563                     if (className.indexOf('month') > -1) {
23564                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23565                     } else {
23566                         var year = parseInt(html, 10) || 0;
23567                         this.viewDate.setUTCFullYear(year);
23568                         
23569                     }
23570                     
23571                     if(this.singleMode){
23572                         this.setValue(this.formatDate(this.viewDate));
23573                         this.hidePopup();
23574                         return;
23575                     }
23576                     
23577                     this.showMode(-1);
23578                     this.fill();
23579                 }
23580                 break;
23581                 
23582             case 'td':
23583                 //Roo.log(className);
23584                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23585                     var day = parseInt(html, 10) || 1;
23586                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23587                         month = (this.viewDate || new Date()).getUTCMonth();
23588
23589                     if (className.indexOf('old') > -1) {
23590                         if(month === 0 ){
23591                             month = 11;
23592                             year -= 1;
23593                         }else{
23594                             month -= 1;
23595                         }
23596                     } else if (className.indexOf('new') > -1) {
23597                         if (month == 11) {
23598                             month = 0;
23599                             year += 1;
23600                         } else {
23601                             month += 1;
23602                         }
23603                     }
23604                     //Roo.log([year,month,day]);
23605                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23606                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23607 //                    this.fill();
23608                     //Roo.log(this.formatDate(this.date));
23609                     this.setValue(this.formatDate(this.date));
23610                     this.hidePopup();
23611                 }
23612                 break;
23613         }
23614     },
23615     
23616     setStartDate: function(startDate)
23617     {
23618         this.startDate = startDate || -Infinity;
23619         if (this.startDate !== -Infinity) {
23620             this.startDate = this.parseDate(this.startDate);
23621         }
23622         this.update();
23623         this.updateNavArrows();
23624     },
23625
23626     setEndDate: function(endDate)
23627     {
23628         this.endDate = endDate || Infinity;
23629         if (this.endDate !== Infinity) {
23630             this.endDate = this.parseDate(this.endDate);
23631         }
23632         this.update();
23633         this.updateNavArrows();
23634     },
23635     
23636     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23637     {
23638         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23639         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23640             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23641         }
23642         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23643             return parseInt(d, 10);
23644         });
23645         this.update();
23646         this.updateNavArrows();
23647     },
23648     
23649     updateNavArrows: function() 
23650     {
23651         if(this.singleMode){
23652             return;
23653         }
23654         
23655         var d = new Date(this.viewDate),
23656         year = d.getUTCFullYear(),
23657         month = d.getUTCMonth();
23658         
23659         Roo.each(this.picker().select('.prev', true).elements, function(v){
23660             v.show();
23661             switch (this.viewMode) {
23662                 case 0:
23663
23664                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23665                         v.hide();
23666                     }
23667                     break;
23668                 case 1:
23669                 case 2:
23670                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23671                         v.hide();
23672                     }
23673                     break;
23674             }
23675         });
23676         
23677         Roo.each(this.picker().select('.next', true).elements, function(v){
23678             v.show();
23679             switch (this.viewMode) {
23680                 case 0:
23681
23682                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23683                         v.hide();
23684                     }
23685                     break;
23686                 case 1:
23687                 case 2:
23688                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23689                         v.hide();
23690                     }
23691                     break;
23692             }
23693         })
23694     },
23695     
23696     moveMonth: function(date, dir)
23697     {
23698         if (!dir) {
23699             return date;
23700         }
23701         var new_date = new Date(date.valueOf()),
23702         day = new_date.getUTCDate(),
23703         month = new_date.getUTCMonth(),
23704         mag = Math.abs(dir),
23705         new_month, test;
23706         dir = dir > 0 ? 1 : -1;
23707         if (mag == 1){
23708             test = dir == -1
23709             // If going back one month, make sure month is not current month
23710             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23711             ? function(){
23712                 return new_date.getUTCMonth() == month;
23713             }
23714             // If going forward one month, make sure month is as expected
23715             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23716             : function(){
23717                 return new_date.getUTCMonth() != new_month;
23718             };
23719             new_month = month + dir;
23720             new_date.setUTCMonth(new_month);
23721             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23722             if (new_month < 0 || new_month > 11) {
23723                 new_month = (new_month + 12) % 12;
23724             }
23725         } else {
23726             // For magnitudes >1, move one month at a time...
23727             for (var i=0; i<mag; i++) {
23728                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23729                 new_date = this.moveMonth(new_date, dir);
23730             }
23731             // ...then reset the day, keeping it in the new month
23732             new_month = new_date.getUTCMonth();
23733             new_date.setUTCDate(day);
23734             test = function(){
23735                 return new_month != new_date.getUTCMonth();
23736             };
23737         }
23738         // Common date-resetting loop -- if date is beyond end of month, make it
23739         // end of month
23740         while (test()){
23741             new_date.setUTCDate(--day);
23742             new_date.setUTCMonth(new_month);
23743         }
23744         return new_date;
23745     },
23746
23747     moveYear: function(date, dir)
23748     {
23749         return this.moveMonth(date, dir*12);
23750     },
23751
23752     dateWithinRange: function(date)
23753     {
23754         return date >= this.startDate && date <= this.endDate;
23755     },
23756
23757     
23758     remove: function() 
23759     {
23760         this.picker().remove();
23761     },
23762     
23763     validateValue : function(value)
23764     {
23765         if(this.getVisibilityEl().hasClass('hidden')){
23766             return true;
23767         }
23768         
23769         if(value.length < 1)  {
23770             if(this.allowBlank){
23771                 return true;
23772             }
23773             return false;
23774         }
23775         
23776         if(value.length < this.minLength){
23777             return false;
23778         }
23779         if(value.length > this.maxLength){
23780             return false;
23781         }
23782         if(this.vtype){
23783             var vt = Roo.form.VTypes;
23784             if(!vt[this.vtype](value, this)){
23785                 return false;
23786             }
23787         }
23788         if(typeof this.validator == "function"){
23789             var msg = this.validator(value);
23790             if(msg !== true){
23791                 return false;
23792             }
23793         }
23794         
23795         if(this.regex && !this.regex.test(value)){
23796             return false;
23797         }
23798         
23799         if(typeof(this.parseDate(value)) == 'undefined'){
23800             return false;
23801         }
23802         
23803         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23804             return false;
23805         }      
23806         
23807         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23808             return false;
23809         } 
23810         
23811         
23812         return true;
23813     },
23814     
23815     reset : function()
23816     {
23817         this.date = this.viewDate = '';
23818         
23819         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23820     }
23821    
23822 });
23823
23824 Roo.apply(Roo.bootstrap.form.DateField,  {
23825     
23826     head : {
23827         tag: 'thead',
23828         cn: [
23829         {
23830             tag: 'tr',
23831             cn: [
23832             {
23833                 tag: 'th',
23834                 cls: 'prev',
23835                 html: '<i class="fa fa-arrow-left"/>'
23836             },
23837             {
23838                 tag: 'th',
23839                 cls: 'switch',
23840                 colspan: '5'
23841             },
23842             {
23843                 tag: 'th',
23844                 cls: 'next',
23845                 html: '<i class="fa fa-arrow-right"/>'
23846             }
23847
23848             ]
23849         }
23850         ]
23851     },
23852     
23853     content : {
23854         tag: 'tbody',
23855         cn: [
23856         {
23857             tag: 'tr',
23858             cn: [
23859             {
23860                 tag: 'td',
23861                 colspan: '7'
23862             }
23863             ]
23864         }
23865         ]
23866     },
23867     
23868     footer : {
23869         tag: 'tfoot',
23870         cn: [
23871         {
23872             tag: 'tr',
23873             cn: [
23874             {
23875                 tag: 'th',
23876                 colspan: '7',
23877                 cls: 'today'
23878             }
23879                     
23880             ]
23881         }
23882         ]
23883     },
23884     
23885     dates:{
23886         en: {
23887             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23888             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23889             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23890             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23891             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23892             today: "Today"
23893         }
23894     },
23895     
23896     modes: [
23897     {
23898         clsName: 'days',
23899         navFnc: 'Month',
23900         navStep: 1
23901     },
23902     {
23903         clsName: 'months',
23904         navFnc: 'FullYear',
23905         navStep: 1
23906     },
23907     {
23908         clsName: 'years',
23909         navFnc: 'FullYear',
23910         navStep: 10
23911     }]
23912 });
23913
23914 Roo.apply(Roo.bootstrap.form.DateField,  {
23915   
23916     template : {
23917         tag: 'div',
23918         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23919         cn: [
23920         {
23921             tag: 'div',
23922             cls: 'datepicker-days',
23923             cn: [
23924             {
23925                 tag: 'table',
23926                 cls: 'table-condensed',
23927                 cn:[
23928                 Roo.bootstrap.form.DateField.head,
23929                 {
23930                     tag: 'tbody'
23931                 },
23932                 Roo.bootstrap.form.DateField.footer
23933                 ]
23934             }
23935             ]
23936         },
23937         {
23938             tag: 'div',
23939             cls: 'datepicker-months',
23940             cn: [
23941             {
23942                 tag: 'table',
23943                 cls: 'table-condensed',
23944                 cn:[
23945                 Roo.bootstrap.form.DateField.head,
23946                 Roo.bootstrap.form.DateField.content,
23947                 Roo.bootstrap.form.DateField.footer
23948                 ]
23949             }
23950             ]
23951         },
23952         {
23953             tag: 'div',
23954             cls: 'datepicker-years',
23955             cn: [
23956             {
23957                 tag: 'table',
23958                 cls: 'table-condensed',
23959                 cn:[
23960                 Roo.bootstrap.form.DateField.head,
23961                 Roo.bootstrap.form.DateField.content,
23962                 Roo.bootstrap.form.DateField.footer
23963                 ]
23964             }
23965             ]
23966         }
23967         ]
23968     }
23969 });
23970
23971  
23972
23973  /*
23974  * - LGPL
23975  *
23976  * TimeField
23977  * 
23978  */
23979
23980 /**
23981  * @class Roo.bootstrap.form.TimeField
23982  * @extends Roo.bootstrap.form.Input
23983  * Bootstrap DateField class
23984  * 
23985  * 
23986  * @constructor
23987  * Create a new TimeField
23988  * @param {Object} config The config object
23989  */
23990
23991 Roo.bootstrap.form.TimeField = function(config){
23992     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23993     this.addEvents({
23994             /**
23995              * @event show
23996              * Fires when this field show.
23997              * @param {Roo.bootstrap.form.DateField} thisthis
23998              * @param {Mixed} date The date value
23999              */
24000             show : true,
24001             /**
24002              * @event show
24003              * Fires when this field hide.
24004              * @param {Roo.bootstrap.form.DateField} this
24005              * @param {Mixed} date The date value
24006              */
24007             hide : true,
24008             /**
24009              * @event select
24010              * Fires when select a date.
24011              * @param {Roo.bootstrap.form.DateField} this
24012              * @param {Mixed} date The date value
24013              */
24014             select : true
24015         });
24016 };
24017
24018 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
24019     
24020     /**
24021      * @cfg {String} format
24022      * The default time format string which can be overriden for localization support.  The format must be
24023      * valid according to {@link Date#parseDate} (defaults to 'H:i').
24024      */
24025     format : "H:i",
24026
24027     getAutoCreate : function()
24028     {
24029         this.after = '<i class="fa far fa-clock"></i>';
24030         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
24031         
24032          
24033     },
24034     onRender: function(ct, position)
24035     {
24036         
24037         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24038                 
24039         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24040         
24041         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24042         
24043         this.pop = this.picker().select('>.datepicker-time',true).first();
24044         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24045         
24046         this.picker().on('mousedown', this.onMousedown, this);
24047         this.picker().on('click', this.onClick, this);
24048         
24049         this.picker().addClass('datepicker-dropdown');
24050     
24051         this.fillTime();
24052         this.update();
24053             
24054         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24055         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24056         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24057         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24058         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24059         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24060
24061     },
24062     
24063     fireKey: function(e){
24064         if (!this.picker().isVisible()){
24065             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24066                 this.show();
24067             }
24068             return;
24069         }
24070
24071         e.preventDefault();
24072         
24073         switch(e.keyCode){
24074             case 27: // escape
24075                 this.hide();
24076                 break;
24077             case 37: // left
24078             case 39: // right
24079                 this.onTogglePeriod();
24080                 break;
24081             case 38: // up
24082                 this.onIncrementMinutes();
24083                 break;
24084             case 40: // down
24085                 this.onDecrementMinutes();
24086                 break;
24087             case 13: // enter
24088             case 9: // tab
24089                 this.setTime();
24090                 break;
24091         }
24092     },
24093     
24094     onClick: function(e) {
24095         e.stopPropagation();
24096         e.preventDefault();
24097     },
24098     
24099     picker : function()
24100     {
24101         return this.pickerEl;
24102     },
24103     
24104     fillTime: function()
24105     {    
24106         var time = this.pop.select('tbody', true).first();
24107         
24108         time.dom.innerHTML = '';
24109         
24110         time.createChild({
24111             tag: 'tr',
24112             cn: [
24113                 {
24114                     tag: 'td',
24115                     cn: [
24116                         {
24117                             tag: 'a',
24118                             href: '#',
24119                             cls: 'btn',
24120                             cn: [
24121                                 {
24122                                     tag: 'i',
24123                                     cls: 'hours-up fa fas fa-chevron-up'
24124                                 }
24125                             ]
24126                         } 
24127                     ]
24128                 },
24129                 {
24130                     tag: 'td',
24131                     cls: 'separator'
24132                 },
24133                 {
24134                     tag: 'td',
24135                     cn: [
24136                         {
24137                             tag: 'a',
24138                             href: '#',
24139                             cls: 'btn',
24140                             cn: [
24141                                 {
24142                                     tag: 'i',
24143                                     cls: 'minutes-up fa fas fa-chevron-up'
24144                                 }
24145                             ]
24146                         }
24147                     ]
24148                 },
24149                 {
24150                     tag: 'td',
24151                     cls: 'separator'
24152                 }
24153             ]
24154         });
24155         
24156         time.createChild({
24157             tag: 'tr',
24158             cn: [
24159                 {
24160                     tag: 'td',
24161                     cn: [
24162                         {
24163                             tag: 'span',
24164                             cls: 'timepicker-hour',
24165                             html: '00'
24166                         }  
24167                     ]
24168                 },
24169                 {
24170                     tag: 'td',
24171                     cls: 'separator',
24172                     html: ':'
24173                 },
24174                 {
24175                     tag: 'td',
24176                     cn: [
24177                         {
24178                             tag: 'span',
24179                             cls: 'timepicker-minute',
24180                             html: '00'
24181                         }  
24182                     ]
24183                 },
24184                 {
24185                     tag: 'td',
24186                     cls: 'separator'
24187                 },
24188                 {
24189                     tag: 'td',
24190                     cn: [
24191                         {
24192                             tag: 'button',
24193                             type: 'button',
24194                             cls: 'btn btn-primary period',
24195                             html: 'AM'
24196                             
24197                         }
24198                     ]
24199                 }
24200             ]
24201         });
24202         
24203         time.createChild({
24204             tag: 'tr',
24205             cn: [
24206                 {
24207                     tag: 'td',
24208                     cn: [
24209                         {
24210                             tag: 'a',
24211                             href: '#',
24212                             cls: 'btn',
24213                             cn: [
24214                                 {
24215                                     tag: 'span',
24216                                     cls: 'hours-down fa fas fa-chevron-down'
24217                                 }
24218                             ]
24219                         }
24220                     ]
24221                 },
24222                 {
24223                     tag: 'td',
24224                     cls: 'separator'
24225                 },
24226                 {
24227                     tag: 'td',
24228                     cn: [
24229                         {
24230                             tag: 'a',
24231                             href: '#',
24232                             cls: 'btn',
24233                             cn: [
24234                                 {
24235                                     tag: 'span',
24236                                     cls: 'minutes-down fa fas fa-chevron-down'
24237                                 }
24238                             ]
24239                         }
24240                     ]
24241                 },
24242                 {
24243                     tag: 'td',
24244                     cls: 'separator'
24245                 }
24246             ]
24247         });
24248         
24249     },
24250     
24251     update: function()
24252     {
24253         
24254         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24255         
24256         this.fill();
24257     },
24258     
24259     fill: function() 
24260     {
24261         var hours = this.time.getHours();
24262         var minutes = this.time.getMinutes();
24263         var period = 'AM';
24264         
24265         if(hours > 11){
24266             period = 'PM';
24267         }
24268         
24269         if(hours == 0){
24270             hours = 12;
24271         }
24272         
24273         
24274         if(hours > 12){
24275             hours = hours - 12;
24276         }
24277         
24278         if(hours < 10){
24279             hours = '0' + hours;
24280         }
24281         
24282         if(minutes < 10){
24283             minutes = '0' + minutes;
24284         }
24285         
24286         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24287         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24288         this.pop.select('button', true).first().dom.innerHTML = period;
24289         
24290     },
24291     
24292     place: function()
24293     {   
24294         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24295         
24296         var cls = ['bottom'];
24297         
24298         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24299             cls.pop();
24300             cls.push('top');
24301         }
24302         
24303         cls.push('right');
24304         
24305         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24306             cls.pop();
24307             cls.push('left');
24308         }
24309         //this.picker().setXY(20000,20000);
24310         this.picker().addClass(cls.join('-'));
24311         
24312         var _this = this;
24313         
24314         Roo.each(cls, function(c){
24315             if(c == 'bottom'){
24316                 (function() {
24317                  //  
24318                 }).defer(200);
24319                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24320                 //_this.picker().setTop(_this.inputEl().getHeight());
24321                 return;
24322             }
24323             if(c == 'top'){
24324                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24325                 
24326                 //_this.picker().setTop(0 - _this.picker().getHeight());
24327                 return;
24328             }
24329             /*
24330             if(c == 'left'){
24331                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24332                 return;
24333             }
24334             if(c == 'right'){
24335                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24336                 return;
24337             }
24338             */
24339         });
24340         
24341     },
24342   
24343     onFocus : function()
24344     {
24345         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24346         this.show();
24347     },
24348     
24349     onBlur : function()
24350     {
24351         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24352         this.hide();
24353     },
24354     
24355     show : function()
24356     {
24357         this.picker().show();
24358         this.pop.show();
24359         this.update();
24360         this.place();
24361         
24362         this.fireEvent('show', this, this.date);
24363     },
24364     
24365     hide : function()
24366     {
24367         this.picker().hide();
24368         this.pop.hide();
24369         
24370         this.fireEvent('hide', this, this.date);
24371     },
24372     
24373     setTime : function()
24374     {
24375         this.hide();
24376         this.setValue(this.time.format(this.format));
24377         
24378         this.fireEvent('select', this, this.date);
24379         
24380         
24381     },
24382     
24383     onMousedown: function(e){
24384         e.stopPropagation();
24385         e.preventDefault();
24386     },
24387     
24388     onIncrementHours: function()
24389     {
24390         Roo.log('onIncrementHours');
24391         this.time = this.time.add(Date.HOUR, 1);
24392         this.update();
24393         
24394     },
24395     
24396     onDecrementHours: function()
24397     {
24398         Roo.log('onDecrementHours');
24399         this.time = this.time.add(Date.HOUR, -1);
24400         this.update();
24401     },
24402     
24403     onIncrementMinutes: function()
24404     {
24405         Roo.log('onIncrementMinutes');
24406         this.time = this.time.add(Date.MINUTE, 1);
24407         this.update();
24408     },
24409     
24410     onDecrementMinutes: function()
24411     {
24412         Roo.log('onDecrementMinutes');
24413         this.time = this.time.add(Date.MINUTE, -1);
24414         this.update();
24415     },
24416     
24417     onTogglePeriod: function()
24418     {
24419         Roo.log('onTogglePeriod');
24420         this.time = this.time.add(Date.HOUR, 12);
24421         this.update();
24422     }
24423     
24424    
24425 });
24426  
24427
24428 Roo.apply(Roo.bootstrap.form.TimeField,  {
24429   
24430     template : {
24431         tag: 'div',
24432         cls: 'datepicker dropdown-menu',
24433         cn: [
24434             {
24435                 tag: 'div',
24436                 cls: 'datepicker-time',
24437                 cn: [
24438                 {
24439                     tag: 'table',
24440                     cls: 'table-condensed',
24441                     cn:[
24442                         {
24443                             tag: 'tbody',
24444                             cn: [
24445                                 {
24446                                     tag: 'tr',
24447                                     cn: [
24448                                     {
24449                                         tag: 'td',
24450                                         colspan: '7'
24451                                     }
24452                                     ]
24453                                 }
24454                             ]
24455                         },
24456                         {
24457                             tag: 'tfoot',
24458                             cn: [
24459                                 {
24460                                     tag: 'tr',
24461                                     cn: [
24462                                     {
24463                                         tag: 'th',
24464                                         colspan: '7',
24465                                         cls: '',
24466                                         cn: [
24467                                             {
24468                                                 tag: 'button',
24469                                                 cls: 'btn btn-info ok',
24470                                                 html: 'OK'
24471                                             }
24472                                         ]
24473                                     }
24474                     
24475                                     ]
24476                                 }
24477                             ]
24478                         }
24479                     ]
24480                 }
24481                 ]
24482             }
24483         ]
24484     }
24485 });
24486
24487  
24488
24489  /*
24490  * - LGPL
24491  *
24492  * MonthField
24493  * 
24494  */
24495
24496 /**
24497  * @class Roo.bootstrap.form.MonthField
24498  * @extends Roo.bootstrap.form.Input
24499  * Bootstrap MonthField class
24500  * 
24501  * @cfg {String} language default en
24502  * 
24503  * @constructor
24504  * Create a new MonthField
24505  * @param {Object} config The config object
24506  */
24507
24508 Roo.bootstrap.form.MonthField = function(config){
24509     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24510     
24511     this.addEvents({
24512         /**
24513          * @event show
24514          * Fires when this field show.
24515          * @param {Roo.bootstrap.form.MonthField} this
24516          * @param {Mixed} date The date value
24517          */
24518         show : true,
24519         /**
24520          * @event show
24521          * Fires when this field hide.
24522          * @param {Roo.bootstrap.form.MonthField} this
24523          * @param {Mixed} date The date value
24524          */
24525         hide : true,
24526         /**
24527          * @event select
24528          * Fires when select a date.
24529          * @param {Roo.bootstrap.form.MonthField} this
24530          * @param {String} oldvalue The old value
24531          * @param {String} newvalue The new value
24532          */
24533         select : true
24534     });
24535 };
24536
24537 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24538     
24539     onRender: function(ct, position)
24540     {
24541         
24542         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24543         
24544         this.language = this.language || 'en';
24545         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24546         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24547         
24548         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24549         this.isInline = false;
24550         this.isInput = true;
24551         this.component = this.el.select('.add-on', true).first() || false;
24552         this.component = (this.component && this.component.length === 0) ? false : this.component;
24553         this.hasInput = this.component && this.inputEL().length;
24554         
24555         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24556         
24557         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24558         
24559         this.picker().on('mousedown', this.onMousedown, this);
24560         this.picker().on('click', this.onClick, this);
24561         
24562         this.picker().addClass('datepicker-dropdown');
24563         
24564         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24565             v.setStyle('width', '189px');
24566         });
24567         
24568         this.fillMonths();
24569         
24570         this.update();
24571         
24572         if(this.isInline) {
24573             this.show();
24574         }
24575         
24576     },
24577     
24578     setValue: function(v, suppressEvent)
24579     {   
24580         var o = this.getValue();
24581         
24582         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24583         
24584         this.update();
24585
24586         if(suppressEvent !== true){
24587             this.fireEvent('select', this, o, v);
24588         }
24589         
24590     },
24591     
24592     getValue: function()
24593     {
24594         return this.value;
24595     },
24596     
24597     onClick: function(e) 
24598     {
24599         e.stopPropagation();
24600         e.preventDefault();
24601         
24602         var target = e.getTarget();
24603         
24604         if(target.nodeName.toLowerCase() === 'i'){
24605             target = Roo.get(target).dom.parentNode;
24606         }
24607         
24608         var nodeName = target.nodeName;
24609         var className = target.className;
24610         var html = target.innerHTML;
24611         
24612         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24613             return;
24614         }
24615         
24616         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24617         
24618         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24619         
24620         this.hide();
24621                         
24622     },
24623     
24624     picker : function()
24625     {
24626         return this.pickerEl;
24627     },
24628     
24629     fillMonths: function()
24630     {    
24631         var i = 0;
24632         var months = this.picker().select('>.datepicker-months td', true).first();
24633         
24634         months.dom.innerHTML = '';
24635         
24636         while (i < 12) {
24637             var month = {
24638                 tag: 'span',
24639                 cls: 'month',
24640                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24641             };
24642             
24643             months.createChild(month);
24644         }
24645         
24646     },
24647     
24648     update: function()
24649     {
24650         var _this = this;
24651         
24652         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24653             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24654         }
24655         
24656         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24657             e.removeClass('active');
24658             
24659             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24660                 e.addClass('active');
24661             }
24662         })
24663     },
24664     
24665     place: function()
24666     {
24667         if(this.isInline) {
24668             return;
24669         }
24670         
24671         this.picker().removeClass(['bottom', 'top']);
24672         
24673         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24674             /*
24675              * place to the top of element!
24676              *
24677              */
24678             
24679             this.picker().addClass('top');
24680             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24681             
24682             return;
24683         }
24684         
24685         this.picker().addClass('bottom');
24686         
24687         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24688     },
24689     
24690     onFocus : function()
24691     {
24692         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24693         this.show();
24694     },
24695     
24696     onBlur : function()
24697     {
24698         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24699         
24700         var d = this.inputEl().getValue();
24701         
24702         this.setValue(d);
24703                 
24704         this.hide();
24705     },
24706     
24707     show : function()
24708     {
24709         this.picker().show();
24710         this.picker().select('>.datepicker-months', true).first().show();
24711         this.update();
24712         this.place();
24713         
24714         this.fireEvent('show', this, this.date);
24715     },
24716     
24717     hide : function()
24718     {
24719         if(this.isInline) {
24720             return;
24721         }
24722         this.picker().hide();
24723         this.fireEvent('hide', this, this.date);
24724         
24725     },
24726     
24727     onMousedown: function(e)
24728     {
24729         e.stopPropagation();
24730         e.preventDefault();
24731     },
24732     
24733     keyup: function(e)
24734     {
24735         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24736         this.update();
24737     },
24738
24739     fireKey: function(e)
24740     {
24741         if (!this.picker().isVisible()){
24742             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24743                 this.show();
24744             }
24745             return;
24746         }
24747         
24748         var dir;
24749         
24750         switch(e.keyCode){
24751             case 27: // escape
24752                 this.hide();
24753                 e.preventDefault();
24754                 break;
24755             case 37: // left
24756             case 39: // right
24757                 dir = e.keyCode == 37 ? -1 : 1;
24758                 
24759                 this.vIndex = this.vIndex + dir;
24760                 
24761                 if(this.vIndex < 0){
24762                     this.vIndex = 0;
24763                 }
24764                 
24765                 if(this.vIndex > 11){
24766                     this.vIndex = 11;
24767                 }
24768                 
24769                 if(isNaN(this.vIndex)){
24770                     this.vIndex = 0;
24771                 }
24772                 
24773                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24774                 
24775                 break;
24776             case 38: // up
24777             case 40: // down
24778                 
24779                 dir = e.keyCode == 38 ? -1 : 1;
24780                 
24781                 this.vIndex = this.vIndex + dir * 4;
24782                 
24783                 if(this.vIndex < 0){
24784                     this.vIndex = 0;
24785                 }
24786                 
24787                 if(this.vIndex > 11){
24788                     this.vIndex = 11;
24789                 }
24790                 
24791                 if(isNaN(this.vIndex)){
24792                     this.vIndex = 0;
24793                 }
24794                 
24795                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24796                 break;
24797                 
24798             case 13: // enter
24799                 
24800                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24801                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24802                 }
24803                 
24804                 this.hide();
24805                 e.preventDefault();
24806                 break;
24807             case 9: // tab
24808                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24809                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24810                 }
24811                 this.hide();
24812                 break;
24813             case 16: // shift
24814             case 17: // ctrl
24815             case 18: // alt
24816                 break;
24817             default :
24818                 this.hide();
24819                 
24820         }
24821     },
24822     
24823     remove: function() 
24824     {
24825         this.picker().remove();
24826     }
24827    
24828 });
24829
24830 Roo.apply(Roo.bootstrap.form.MonthField,  {
24831     
24832     content : {
24833         tag: 'tbody',
24834         cn: [
24835         {
24836             tag: 'tr',
24837             cn: [
24838             {
24839                 tag: 'td',
24840                 colspan: '7'
24841             }
24842             ]
24843         }
24844         ]
24845     },
24846     
24847     dates:{
24848         en: {
24849             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24850             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24851         }
24852     }
24853 });
24854
24855 Roo.apply(Roo.bootstrap.form.MonthField,  {
24856   
24857     template : {
24858         tag: 'div',
24859         cls: 'datepicker dropdown-menu roo-dynamic',
24860         cn: [
24861             {
24862                 tag: 'div',
24863                 cls: 'datepicker-months',
24864                 cn: [
24865                 {
24866                     tag: 'table',
24867                     cls: 'table-condensed',
24868                     cn:[
24869                         Roo.bootstrap.form.DateField.content
24870                     ]
24871                 }
24872                 ]
24873             }
24874         ]
24875     }
24876 });
24877
24878  
24879
24880  
24881  /*
24882  * - LGPL
24883  *
24884  * CheckBox
24885  * 
24886  */
24887
24888 /**
24889  * @class Roo.bootstrap.form.CheckBox
24890  * @extends Roo.bootstrap.form.Input
24891  * Bootstrap CheckBox class
24892  * 
24893  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24894  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24895  * @cfg {String} boxLabel The text that appears beside the checkbox
24896  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24897  * @cfg {Boolean} checked initnal the element
24898  * @cfg {Boolean} inline inline the element (default false)
24899  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24900  * @cfg {String} tooltip label tooltip
24901  * 
24902  * @constructor
24903  * Create a new CheckBox
24904  * @param {Object} config The config object
24905  */
24906
24907 Roo.bootstrap.form.CheckBox = function(config){
24908     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24909    
24910     this.addEvents({
24911         /**
24912         * @event check
24913         * Fires when the element is checked or unchecked.
24914         * @param {Roo.bootstrap.form.CheckBox} this This input
24915         * @param {Boolean} checked The new checked value
24916         */
24917        check : true,
24918        /**
24919         * @event click
24920         * Fires when the element is click.
24921         * @param {Roo.bootstrap.form.CheckBox} this This input
24922         */
24923        click : true
24924     });
24925     
24926 };
24927
24928 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24929   
24930     inputType: 'checkbox',
24931     inputValue: 1,
24932     valueOff: 0,
24933     boxLabel: false,
24934     checked: false,
24935     weight : false,
24936     inline: false,
24937     tooltip : '',
24938     
24939     // checkbox success does not make any sense really.. 
24940     invalidClass : "",
24941     validClass : "",
24942     
24943     
24944     getAutoCreate : function()
24945     {
24946         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24947         
24948         var id = Roo.id();
24949         
24950         var cfg = {};
24951         
24952         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24953         
24954         if(this.inline){
24955             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24956         }
24957         
24958         var input =  {
24959             tag: 'input',
24960             id : id,
24961             type : this.inputType,
24962             value : this.inputValue,
24963             cls : 'roo-' + this.inputType, //'form-box',
24964             placeholder : this.placeholder || ''
24965             
24966         };
24967         
24968         if(this.inputType != 'radio'){
24969             var hidden =  {
24970                 tag: 'input',
24971                 type : 'hidden',
24972                 cls : 'roo-hidden-value',
24973                 value : this.checked ? this.inputValue : this.valueOff
24974             };
24975         }
24976         
24977             
24978         if (this.weight) { // Validity check?
24979             cfg.cls += " " + this.inputType + "-" + this.weight;
24980         }
24981         
24982         if (this.disabled) {
24983             input.disabled=true;
24984         }
24985         
24986         if(this.checked){
24987             input.checked = this.checked;
24988         }
24989         
24990         if (this.name) {
24991             
24992             input.name = this.name;
24993             
24994             if(this.inputType != 'radio'){
24995                 hidden.name = this.name;
24996                 input.name = '_hidden_' + this.name;
24997             }
24998         }
24999         
25000         if (this.size) {
25001             input.cls += ' input-' + this.size;
25002         }
25003         
25004         var settings=this;
25005         
25006         ['xs','sm','md','lg'].map(function(size){
25007             if (settings[size]) {
25008                 cfg.cls += ' col-' + size + '-' + settings[size];
25009             }
25010         });
25011         
25012         var inputblock = input;
25013          
25014         if (this.before || this.after) {
25015             
25016             inputblock = {
25017                 cls : 'input-group',
25018                 cn :  [] 
25019             };
25020             
25021             if (this.before) {
25022                 inputblock.cn.push({
25023                     tag :'span',
25024                     cls : 'input-group-addon',
25025                     html : this.before
25026                 });
25027             }
25028             
25029             inputblock.cn.push(input);
25030             
25031             if(this.inputType != 'radio'){
25032                 inputblock.cn.push(hidden);
25033             }
25034             
25035             if (this.after) {
25036                 inputblock.cn.push({
25037                     tag :'span',
25038                     cls : 'input-group-addon',
25039                     html : this.after
25040                 });
25041             }
25042             
25043         }
25044         var boxLabelCfg = false;
25045         
25046         if(this.boxLabel){
25047            
25048             boxLabelCfg = {
25049                 tag: 'label',
25050                 //'for': id, // box label is handled by onclick - so no for...
25051                 cls: 'box-label',
25052                 html: this.boxLabel
25053             };
25054             if(this.tooltip){
25055                 boxLabelCfg.tooltip = this.tooltip;
25056             }
25057              
25058         }
25059         
25060         
25061         if (align ==='left' && this.fieldLabel.length) {
25062 //                Roo.log("left and has label");
25063             cfg.cn = [
25064                 {
25065                     tag: 'label',
25066                     'for' :  id,
25067                     cls : 'control-label',
25068                     html : this.fieldLabel
25069                 },
25070                 {
25071                     cls : "", 
25072                     cn: [
25073                         inputblock
25074                     ]
25075                 }
25076             ];
25077             
25078             if (boxLabelCfg) {
25079                 cfg.cn[1].cn.push(boxLabelCfg);
25080             }
25081             
25082             if(this.labelWidth > 12){
25083                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25084             }
25085             
25086             if(this.labelWidth < 13 && this.labelmd == 0){
25087                 this.labelmd = this.labelWidth;
25088             }
25089             
25090             if(this.labellg > 0){
25091                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25092                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25093             }
25094             
25095             if(this.labelmd > 0){
25096                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25097                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25098             }
25099             
25100             if(this.labelsm > 0){
25101                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25102                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25103             }
25104             
25105             if(this.labelxs > 0){
25106                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25107                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25108             }
25109             
25110         } else if ( this.fieldLabel.length) {
25111 //                Roo.log(" label");
25112                 cfg.cn = [
25113                    
25114                     {
25115                         tag: this.boxLabel ? 'span' : 'label',
25116                         'for': id,
25117                         cls: 'control-label box-input-label',
25118                         //cls : 'input-group-addon',
25119                         html : this.fieldLabel
25120                     },
25121                     
25122                     inputblock
25123                     
25124                 ];
25125                 if (boxLabelCfg) {
25126                     cfg.cn.push(boxLabelCfg);
25127                 }
25128
25129         } else {
25130             
25131 //                Roo.log(" no label && no align");
25132                 cfg.cn = [  inputblock ] ;
25133                 if (boxLabelCfg) {
25134                     cfg.cn.push(boxLabelCfg);
25135                 }
25136
25137                 
25138         }
25139         
25140        
25141         
25142         if(this.inputType != 'radio'){
25143             cfg.cn.push(hidden);
25144         }
25145         
25146         return cfg;
25147         
25148     },
25149     
25150     /**
25151      * return the real input element.
25152      */
25153     inputEl: function ()
25154     {
25155         return this.el.select('input.roo-' + this.inputType,true).first();
25156     },
25157     hiddenEl: function ()
25158     {
25159         return this.el.select('input.roo-hidden-value',true).first();
25160     },
25161     
25162     labelEl: function()
25163     {
25164         return this.el.select('label.control-label',true).first();
25165     },
25166     /* depricated... */
25167     
25168     label: function()
25169     {
25170         return this.labelEl();
25171     },
25172     
25173     boxLabelEl: function()
25174     {
25175         return this.el.select('label.box-label',true).first();
25176     },
25177     
25178     initEvents : function()
25179     {
25180 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25181         
25182         this.inputEl().on('click', this.onClick,  this);
25183         
25184         if (this.boxLabel) { 
25185             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25186         }
25187         
25188         this.startValue = this.getValue();
25189         
25190         if(this.groupId){
25191             Roo.bootstrap.form.CheckBox.register(this);
25192         }
25193     },
25194     
25195     onClick : function(e)
25196     {   
25197         if(this.fireEvent('click', this, e) !== false){
25198             this.setChecked(!this.checked);
25199         }
25200         
25201     },
25202     
25203     setChecked : function(state,suppressEvent)
25204     {
25205         this.startValue = this.getValue();
25206
25207         if(this.inputType == 'radio'){
25208             
25209             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25210                 e.dom.checked = false;
25211             });
25212             
25213             this.inputEl().dom.checked = true;
25214             
25215             this.inputEl().dom.value = this.inputValue;
25216             
25217             if(suppressEvent !== true){
25218                 this.fireEvent('check', this, true);
25219             }
25220             
25221             this.validate();
25222             
25223             return;
25224         }
25225         
25226         this.checked = state;
25227         
25228         this.inputEl().dom.checked = state;
25229         
25230         
25231         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25232         
25233         if(suppressEvent !== true){
25234             this.fireEvent('check', this, state);
25235         }
25236         
25237         this.validate();
25238     },
25239     
25240     getValue : function()
25241     {
25242         if(this.inputType == 'radio'){
25243             return this.getGroupValue();
25244         }
25245         
25246         return this.hiddenEl().dom.value;
25247         
25248     },
25249     
25250     getGroupValue : function()
25251     {
25252         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25253             return '';
25254         }
25255         
25256         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25257     },
25258     
25259     setValue : function(v,suppressEvent)
25260     {
25261         if(this.inputType == 'radio'){
25262             this.setGroupValue(v, suppressEvent);
25263             return;
25264         }
25265         
25266         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25267         
25268         this.validate();
25269     },
25270     
25271     setGroupValue : function(v, suppressEvent)
25272     {
25273         this.startValue = this.getValue();
25274         
25275         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25276             e.dom.checked = false;
25277             
25278             if(e.dom.value == v){
25279                 e.dom.checked = true;
25280             }
25281         });
25282         
25283         if(suppressEvent !== true){
25284             this.fireEvent('check', this, true);
25285         }
25286
25287         this.validate();
25288         
25289         return;
25290     },
25291     
25292     validate : function()
25293     {
25294         if(this.getVisibilityEl().hasClass('hidden')){
25295             return true;
25296         }
25297         
25298         if(
25299                 this.disabled || 
25300                 (this.inputType == 'radio' && this.validateRadio()) ||
25301                 (this.inputType == 'checkbox' && this.validateCheckbox())
25302         ){
25303             this.markValid();
25304             return true;
25305         }
25306         
25307         this.markInvalid();
25308         return false;
25309     },
25310     
25311     validateRadio : function()
25312     {
25313         if(this.getVisibilityEl().hasClass('hidden')){
25314             return true;
25315         }
25316         
25317         if(this.allowBlank){
25318             return true;
25319         }
25320         
25321         var valid = false;
25322         
25323         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25324             if(!e.dom.checked){
25325                 return;
25326             }
25327             
25328             valid = true;
25329             
25330             return false;
25331         });
25332         
25333         return valid;
25334     },
25335     
25336     validateCheckbox : function()
25337     {
25338         if(!this.groupId){
25339             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25340             //return (this.getValue() == this.inputValue) ? true : false;
25341         }
25342         
25343         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25344         
25345         if(!group){
25346             return false;
25347         }
25348         
25349         var r = false;
25350         
25351         for(var i in group){
25352             if(group[i].el.isVisible(true)){
25353                 r = false;
25354                 break;
25355             }
25356             
25357             r = true;
25358         }
25359         
25360         for(var i in group){
25361             if(r){
25362                 break;
25363             }
25364             
25365             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25366         }
25367         
25368         return r;
25369     },
25370     
25371     /**
25372      * Mark this field as valid
25373      */
25374     markValid : function()
25375     {
25376         var _this = this;
25377         
25378         this.fireEvent('valid', this);
25379         
25380         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25381         
25382         if(this.groupId){
25383             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25384         }
25385         
25386         if(label){
25387             label.markValid();
25388         }
25389
25390         if(this.inputType == 'radio'){
25391             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25392                 var fg = e.findParent('.form-group', false, true);
25393                 if (Roo.bootstrap.version == 3) {
25394                     fg.removeClass([_this.invalidClass, _this.validClass]);
25395                     fg.addClass(_this.validClass);
25396                 } else {
25397                     fg.removeClass(['is-valid', 'is-invalid']);
25398                     fg.addClass('is-valid');
25399                 }
25400             });
25401             
25402             return;
25403         }
25404
25405         if(!this.groupId){
25406             var fg = this.el.findParent('.form-group', false, true);
25407             if (Roo.bootstrap.version == 3) {
25408                 fg.removeClass([this.invalidClass, this.validClass]);
25409                 fg.addClass(this.validClass);
25410             } else {
25411                 fg.removeClass(['is-valid', 'is-invalid']);
25412                 fg.addClass('is-valid');
25413             }
25414             return;
25415         }
25416         
25417         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25418         
25419         if(!group){
25420             return;
25421         }
25422         
25423         for(var i in group){
25424             var fg = group[i].el.findParent('.form-group', false, true);
25425             if (Roo.bootstrap.version == 3) {
25426                 fg.removeClass([this.invalidClass, this.validClass]);
25427                 fg.addClass(this.validClass);
25428             } else {
25429                 fg.removeClass(['is-valid', 'is-invalid']);
25430                 fg.addClass('is-valid');
25431             }
25432         }
25433     },
25434     
25435      /**
25436      * Mark this field as invalid
25437      * @param {String} msg The validation message
25438      */
25439     markInvalid : function(msg)
25440     {
25441         if(this.allowBlank){
25442             return;
25443         }
25444         
25445         var _this = this;
25446         
25447         this.fireEvent('invalid', this, msg);
25448         
25449         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25450         
25451         if(this.groupId){
25452             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25453         }
25454         
25455         if(label){
25456             label.markInvalid();
25457         }
25458             
25459         if(this.inputType == 'radio'){
25460             
25461             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25462                 var fg = e.findParent('.form-group', false, true);
25463                 if (Roo.bootstrap.version == 3) {
25464                     fg.removeClass([_this.invalidClass, _this.validClass]);
25465                     fg.addClass(_this.invalidClass);
25466                 } else {
25467                     fg.removeClass(['is-invalid', 'is-valid']);
25468                     fg.addClass('is-invalid');
25469                 }
25470             });
25471             
25472             return;
25473         }
25474         
25475         if(!this.groupId){
25476             var fg = this.el.findParent('.form-group', false, true);
25477             if (Roo.bootstrap.version == 3) {
25478                 fg.removeClass([_this.invalidClass, _this.validClass]);
25479                 fg.addClass(_this.invalidClass);
25480             } else {
25481                 fg.removeClass(['is-invalid', 'is-valid']);
25482                 fg.addClass('is-invalid');
25483             }
25484             return;
25485         }
25486         
25487         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25488         
25489         if(!group){
25490             return;
25491         }
25492         
25493         for(var i in group){
25494             var fg = group[i].el.findParent('.form-group', false, true);
25495             if (Roo.bootstrap.version == 3) {
25496                 fg.removeClass([_this.invalidClass, _this.validClass]);
25497                 fg.addClass(_this.invalidClass);
25498             } else {
25499                 fg.removeClass(['is-invalid', 'is-valid']);
25500                 fg.addClass('is-invalid');
25501             }
25502         }
25503         
25504     },
25505     
25506     clearInvalid : function()
25507     {
25508         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25509         
25510         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25511         
25512         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25513         
25514         if (label && label.iconEl) {
25515             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25516             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25517         }
25518     },
25519     
25520     disable : function()
25521     {
25522         if(this.inputType != 'radio'){
25523             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25524             return;
25525         }
25526         
25527         var _this = this;
25528         
25529         if(this.rendered){
25530             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25531                 _this.getActionEl().addClass(this.disabledClass);
25532                 e.dom.disabled = true;
25533             });
25534         }
25535         
25536         this.disabled = true;
25537         this.fireEvent("disable", this);
25538         return this;
25539     },
25540
25541     enable : function()
25542     {
25543         if(this.inputType != 'radio'){
25544             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25545             return;
25546         }
25547         
25548         var _this = this;
25549         
25550         if(this.rendered){
25551             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25552                 _this.getActionEl().removeClass(this.disabledClass);
25553                 e.dom.disabled = false;
25554             });
25555         }
25556         
25557         this.disabled = false;
25558         this.fireEvent("enable", this);
25559         return this;
25560     },
25561     
25562     setBoxLabel : function(v)
25563     {
25564         this.boxLabel = v;
25565         
25566         if(this.rendered){
25567             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25568         }
25569     }
25570
25571 });
25572
25573 Roo.apply(Roo.bootstrap.form.CheckBox, {
25574     
25575     groups: {},
25576     
25577      /**
25578     * register a CheckBox Group
25579     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25580     */
25581     register : function(checkbox)
25582     {
25583         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25584             this.groups[checkbox.groupId] = {};
25585         }
25586         
25587         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25588             return;
25589         }
25590         
25591         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25592         
25593     },
25594     /**
25595     * fetch a CheckBox Group based on the group ID
25596     * @param {string} the group ID
25597     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25598     */
25599     get: function(groupId) {
25600         if (typeof(this.groups[groupId]) == 'undefined') {
25601             return false;
25602         }
25603         
25604         return this.groups[groupId] ;
25605     }
25606     
25607     
25608 });
25609 /*
25610  * - LGPL
25611  *
25612  * RadioItem
25613  * 
25614  */
25615
25616 /**
25617  * @class Roo.bootstrap.form.Radio
25618  * @extends Roo.bootstrap.Component
25619  * Bootstrap Radio class
25620  * @cfg {String} boxLabel - the label associated
25621  * @cfg {String} value - the value of radio
25622  * 
25623  * @constructor
25624  * Create a new Radio
25625  * @param {Object} config The config object
25626  */
25627 Roo.bootstrap.form.Radio = function(config){
25628     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25629     
25630 };
25631
25632 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25633     
25634     boxLabel : '',
25635     
25636     value : '',
25637     
25638     getAutoCreate : function()
25639     {
25640         var cfg = {
25641             tag : 'div',
25642             cls : 'form-group radio',
25643             cn : [
25644                 {
25645                     tag : 'label',
25646                     cls : 'box-label',
25647                     html : this.boxLabel
25648                 }
25649             ]
25650         };
25651         
25652         return cfg;
25653     },
25654     
25655     initEvents : function() 
25656     {
25657         this.parent().register(this);
25658         
25659         this.el.on('click', this.onClick, this);
25660         
25661     },
25662     
25663     onClick : function(e)
25664     {
25665         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25666             this.setChecked(true);
25667         }
25668     },
25669     
25670     setChecked : function(state, suppressEvent)
25671     {
25672         this.parent().setValue(this.value, suppressEvent);
25673         
25674     },
25675     
25676     setBoxLabel : function(v)
25677     {
25678         this.boxLabel = v;
25679         
25680         if(this.rendered){
25681             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25682         }
25683     }
25684     
25685 });
25686  
25687
25688  /*
25689  * - LGPL
25690  *
25691  * Input
25692  * 
25693  */
25694
25695 /**
25696  * @class Roo.bootstrap.form.SecurePass
25697  * @extends Roo.bootstrap.form.Input
25698  * Bootstrap SecurePass class
25699  *
25700  * 
25701  * @constructor
25702  * Create a new SecurePass
25703  * @param {Object} config The config object
25704  */
25705  
25706 Roo.bootstrap.form.SecurePass = function (config) {
25707     // these go here, so the translation tool can replace them..
25708     this.errors = {
25709         PwdEmpty: "Please type a password, and then retype it to confirm.",
25710         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25711         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25712         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25713         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25714         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25715         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25716         TooWeak: "Your password is Too Weak."
25717     },
25718     this.meterLabel = "Password strength:";
25719     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25720     this.meterClass = [
25721         "roo-password-meter-tooweak", 
25722         "roo-password-meter-weak", 
25723         "roo-password-meter-medium", 
25724         "roo-password-meter-strong", 
25725         "roo-password-meter-grey"
25726     ];
25727     
25728     this.errors = {};
25729     
25730     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25731 }
25732
25733 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25734     /**
25735      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25736      * {
25737      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25738      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25739      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25740      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25741      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25742      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25743      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25744      * })
25745      */
25746     // private
25747     
25748     meterWidth: 300,
25749     errorMsg :'',    
25750     errors: false,
25751     imageRoot: '/',
25752     /**
25753      * @cfg {String/Object} Label for the strength meter (defaults to
25754      * 'Password strength:')
25755      */
25756     // private
25757     meterLabel: '',
25758     /**
25759      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25760      * ['Weak', 'Medium', 'Strong'])
25761      */
25762     // private    
25763     pwdStrengths: false,    
25764     // private
25765     strength: 0,
25766     // private
25767     _lastPwd: null,
25768     // private
25769     kCapitalLetter: 0,
25770     kSmallLetter: 1,
25771     kDigit: 2,
25772     kPunctuation: 3,
25773     
25774     insecure: false,
25775     // private
25776     initEvents: function ()
25777     {
25778         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25779
25780         if (this.el.is('input[type=password]') && Roo.isSafari) {
25781             this.el.on('keydown', this.SafariOnKeyDown, this);
25782         }
25783
25784         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25785     },
25786     // private
25787     onRender: function (ct, position)
25788     {
25789         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25790         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25791         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25792
25793         this.trigger.createChild({
25794                    cn: [
25795                     {
25796                     //id: 'PwdMeter',
25797                     tag: 'div',
25798                     cls: 'roo-password-meter-grey col-xs-12',
25799                     style: {
25800                         //width: 0,
25801                         //width: this.meterWidth + 'px'                                                
25802                         }
25803                     },
25804                     {                            
25805                          cls: 'roo-password-meter-text'                          
25806                     }
25807                 ]            
25808         });
25809
25810          
25811         if (this.hideTrigger) {
25812             this.trigger.setDisplayed(false);
25813         }
25814         this.setSize(this.width || '', this.height || '');
25815     },
25816     // private
25817     onDestroy: function ()
25818     {
25819         if (this.trigger) {
25820             this.trigger.removeAllListeners();
25821             this.trigger.remove();
25822         }
25823         if (this.wrap) {
25824             this.wrap.remove();
25825         }
25826         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25827     },
25828     // private
25829     checkStrength: function ()
25830     {
25831         var pwd = this.inputEl().getValue();
25832         if (pwd == this._lastPwd) {
25833             return;
25834         }
25835
25836         var strength;
25837         if (this.ClientSideStrongPassword(pwd)) {
25838             strength = 3;
25839         } else if (this.ClientSideMediumPassword(pwd)) {
25840             strength = 2;
25841         } else if (this.ClientSideWeakPassword(pwd)) {
25842             strength = 1;
25843         } else {
25844             strength = 0;
25845         }
25846         
25847         Roo.log('strength1: ' + strength);
25848         
25849         //var pm = this.trigger.child('div/div/div').dom;
25850         var pm = this.trigger.child('div/div');
25851         pm.removeClass(this.meterClass);
25852         pm.addClass(this.meterClass[strength]);
25853                 
25854         
25855         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25856                 
25857         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25858         
25859         this._lastPwd = pwd;
25860     },
25861     reset: function ()
25862     {
25863         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25864         
25865         this._lastPwd = '';
25866         
25867         var pm = this.trigger.child('div/div');
25868         pm.removeClass(this.meterClass);
25869         pm.addClass('roo-password-meter-grey');        
25870         
25871         
25872         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25873         
25874         pt.innerHTML = '';
25875         this.inputEl().dom.type='password';
25876     },
25877     // private
25878     validateValue: function (value)
25879     {
25880         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25881             return false;
25882         }
25883         if (value.length == 0) {
25884             if (this.allowBlank) {
25885                 this.clearInvalid();
25886                 return true;
25887             }
25888
25889             this.markInvalid(this.errors.PwdEmpty);
25890             this.errorMsg = this.errors.PwdEmpty;
25891             return false;
25892         }
25893         
25894         if(this.insecure){
25895             return true;
25896         }
25897         
25898         if (!value.match(/[\x21-\x7e]+/)) {
25899             this.markInvalid(this.errors.PwdBadChar);
25900             this.errorMsg = this.errors.PwdBadChar;
25901             return false;
25902         }
25903         if (value.length < 6) {
25904             this.markInvalid(this.errors.PwdShort);
25905             this.errorMsg = this.errors.PwdShort;
25906             return false;
25907         }
25908         if (value.length > 16) {
25909             this.markInvalid(this.errors.PwdLong);
25910             this.errorMsg = this.errors.PwdLong;
25911             return false;
25912         }
25913         var strength;
25914         if (this.ClientSideStrongPassword(value)) {
25915             strength = 3;
25916         } else if (this.ClientSideMediumPassword(value)) {
25917             strength = 2;
25918         } else if (this.ClientSideWeakPassword(value)) {
25919             strength = 1;
25920         } else {
25921             strength = 0;
25922         }
25923
25924         
25925         if (strength < 2) {
25926             //this.markInvalid(this.errors.TooWeak);
25927             this.errorMsg = this.errors.TooWeak;
25928             //return false;
25929         }
25930         
25931         
25932         console.log('strength2: ' + strength);
25933         
25934         //var pm = this.trigger.child('div/div/div').dom;
25935         
25936         var pm = this.trigger.child('div/div');
25937         pm.removeClass(this.meterClass);
25938         pm.addClass(this.meterClass[strength]);
25939                 
25940         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25941                 
25942         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25943         
25944         this.errorMsg = ''; 
25945         return true;
25946     },
25947     // private
25948     CharacterSetChecks: function (type)
25949     {
25950         this.type = type;
25951         this.fResult = false;
25952     },
25953     // private
25954     isctype: function (character, type)
25955     {
25956         switch (type) {  
25957             case this.kCapitalLetter:
25958                 if (character >= 'A' && character <= 'Z') {
25959                     return true;
25960                 }
25961                 break;
25962             
25963             case this.kSmallLetter:
25964                 if (character >= 'a' && character <= 'z') {
25965                     return true;
25966                 }
25967                 break;
25968             
25969             case this.kDigit:
25970                 if (character >= '0' && character <= '9') {
25971                     return true;
25972                 }
25973                 break;
25974             
25975             case this.kPunctuation:
25976                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25977                     return true;
25978                 }
25979                 break;
25980             
25981             default:
25982                 return false;
25983         }
25984
25985     },
25986     // private
25987     IsLongEnough: function (pwd, size)
25988     {
25989         return !(pwd == null || isNaN(size) || pwd.length < size);
25990     },
25991     // private
25992     SpansEnoughCharacterSets: function (word, nb)
25993     {
25994         if (!this.IsLongEnough(word, nb))
25995         {
25996             return false;
25997         }
25998
25999         var characterSetChecks = new Array(
26000             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
26001             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
26002         );
26003         
26004         for (var index = 0; index < word.length; ++index) {
26005             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26006                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
26007                     characterSetChecks[nCharSet].fResult = true;
26008                     break;
26009                 }
26010             }
26011         }
26012
26013         var nCharSets = 0;
26014         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26015             if (characterSetChecks[nCharSet].fResult) {
26016                 ++nCharSets;
26017             }
26018         }
26019
26020         if (nCharSets < nb) {
26021             return false;
26022         }
26023         return true;
26024     },
26025     // private
26026     ClientSideStrongPassword: function (pwd)
26027     {
26028         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
26029     },
26030     // private
26031     ClientSideMediumPassword: function (pwd)
26032     {
26033         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
26034     },
26035     // private
26036     ClientSideWeakPassword: function (pwd)
26037     {
26038         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26039     }
26040           
26041 });
26042 Roo.htmleditor = {};
26043  
26044 /**
26045  * @class Roo.htmleditor.Filter
26046  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26047  * @cfg {DomElement} node The node to iterate and filter
26048  * @cfg {boolean|String|Array} tag Tags to replace 
26049  * @constructor
26050  * Create a new Filter.
26051  * @param {Object} config Configuration options
26052  */
26053
26054
26055
26056 Roo.htmleditor.Filter = function(cfg) {
26057     Roo.apply(this.cfg);
26058     // this does not actually call walk as it's really just a abstract class
26059 }
26060
26061
26062 Roo.htmleditor.Filter.prototype = {
26063     
26064     node: false,
26065     
26066     tag: false,
26067
26068     // overrride to do replace comments.
26069     replaceComment : false,
26070     
26071     // overrride to do replace or do stuff with tags..
26072     replaceTag : false,
26073     
26074     walk : function(dom)
26075     {
26076         Roo.each( Array.from(dom.childNodes), function( e ) {
26077             switch(true) {
26078                 
26079                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26080                     this.replaceComment(e);
26081                     return;
26082                 
26083                 case e.nodeType != 1: //not a node.
26084                     return;
26085                 
26086                 case this.tag === true: // everything
26087                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26088                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26089                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26090                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26091                     if (this.replaceTag && false === this.replaceTag(e)) {
26092                         return;
26093                     }
26094                     if (e.hasChildNodes()) {
26095                         this.walk(e);
26096                     }
26097                     return;
26098                 
26099                 default:    // tags .. that do not match.
26100                     if (e.hasChildNodes()) {
26101                         this.walk(e);
26102                     }
26103             }
26104             
26105         }, this);
26106         
26107     },
26108     
26109     
26110     removeNodeKeepChildren : function( node)
26111     {
26112     
26113         ar = Array.from(node.childNodes);
26114         for (var i = 0; i < ar.length; i++) {
26115          
26116             node.removeChild(ar[i]);
26117             // what if we need to walk these???
26118             node.parentNode.insertBefore(ar[i], node);
26119            
26120         }
26121         node.parentNode.removeChild(node);
26122     }
26123 }; 
26124
26125 /**
26126  * @class Roo.htmleditor.FilterAttributes
26127  * clean attributes and  styles including http:// etc.. in attribute
26128  * @constructor
26129 * Run a new Attribute Filter
26130 * @param {Object} config Configuration options
26131  */
26132 Roo.htmleditor.FilterAttributes = function(cfg)
26133 {
26134     Roo.apply(this, cfg);
26135     this.attrib_black = this.attrib_black || [];
26136     this.attrib_white = this.attrib_white || [];
26137
26138     this.attrib_clean = this.attrib_clean || [];
26139     this.style_white = this.style_white || [];
26140     this.style_black = this.style_black || [];
26141     this.walk(cfg.node);
26142 }
26143
26144 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26145 {
26146     tag: true, // all tags
26147     
26148     attrib_black : false, // array
26149     attrib_clean : false,
26150     attrib_white : false,
26151
26152     style_white : false,
26153     style_black : false,
26154      
26155      
26156     replaceTag : function(node)
26157     {
26158         if (!node.attributes || !node.attributes.length) {
26159             return true;
26160         }
26161         
26162         for (var i = node.attributes.length-1; i > -1 ; i--) {
26163             var a = node.attributes[i];
26164             //console.log(a);
26165             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26166                 node.removeAttribute(a.name);
26167                 continue;
26168             }
26169             
26170             
26171             
26172             if (a.name.toLowerCase().substr(0,2)=='on')  {
26173                 node.removeAttribute(a.name);
26174                 continue;
26175             }
26176             
26177             
26178             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26179                 node.removeAttribute(a.name);
26180                 continue;
26181             }
26182             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26183                 this.cleanAttr(node,a.name,a.value); // fixme..
26184                 continue;
26185             }
26186             if (a.name == 'style') {
26187                 this.cleanStyle(node,a.name,a.value);
26188                 continue;
26189             }
26190             /// clean up MS crap..
26191             // tecnically this should be a list of valid class'es..
26192             
26193             
26194             if (a.name == 'class') {
26195                 if (a.value.match(/^Mso/)) {
26196                     node.removeAttribute('class');
26197                 }
26198                 
26199                 if (a.value.match(/^body$/)) {
26200                     node.removeAttribute('class');
26201                 }
26202                 continue;
26203             }
26204             
26205             
26206             // style cleanup!?
26207             // class cleanup?
26208             
26209         }
26210         return true; // clean children
26211     },
26212         
26213     cleanAttr: function(node, n,v)
26214     {
26215         
26216         if (v.match(/^\./) || v.match(/^\//)) {
26217             return;
26218         }
26219         if (v.match(/^(http|https):\/\//)
26220             || v.match(/^mailto:/) 
26221             || v.match(/^ftp:/)
26222             || v.match(/^data:/)
26223             ) {
26224             return;
26225         }
26226         if (v.match(/^#/)) {
26227             return;
26228         }
26229         if (v.match(/^\{/)) { // allow template editing.
26230             return;
26231         }
26232 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26233         node.removeAttribute(n);
26234         
26235     },
26236     cleanStyle : function(node,  n,v)
26237     {
26238         if (v.match(/expression/)) { //XSS?? should we even bother..
26239             node.removeAttribute(n);
26240             return;
26241         }
26242         
26243         var parts = v.split(/;/);
26244         var clean = [];
26245         
26246         Roo.each(parts, function(p) {
26247             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26248             if (!p.length) {
26249                 return true;
26250             }
26251             var l = p.split(':').shift().replace(/\s+/g,'');
26252             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26253             
26254             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26255                 return true;
26256             }
26257             //Roo.log()
26258             // only allow 'c whitelisted system attributes'
26259             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26260                 return true;
26261             }
26262             
26263             
26264             clean.push(p);
26265             return true;
26266         },this);
26267         if (clean.length) { 
26268             node.setAttribute(n, clean.join(';'));
26269         } else {
26270             node.removeAttribute(n);
26271         }
26272         
26273     }
26274         
26275         
26276         
26277     
26278 });/**
26279  * @class Roo.htmleditor.FilterBlack
26280  * remove blacklisted elements.
26281  * @constructor
26282  * Run a new Blacklisted Filter
26283  * @param {Object} config Configuration options
26284  */
26285
26286 Roo.htmleditor.FilterBlack = function(cfg)
26287 {
26288     Roo.apply(this, cfg);
26289     this.walk(cfg.node);
26290 }
26291
26292 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26293 {
26294     tag : true, // all elements.
26295    
26296     replaceTag : function(n)
26297     {
26298         n.parentNode.removeChild(n);
26299     }
26300 });
26301 /**
26302  * @class Roo.htmleditor.FilterComment
26303  * remove comments.
26304  * @constructor
26305 * Run a new Comments Filter
26306 * @param {Object} config Configuration options
26307  */
26308 Roo.htmleditor.FilterComment = function(cfg)
26309 {
26310     this.walk(cfg.node);
26311 }
26312
26313 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26314 {
26315   
26316     replaceComment : function(n)
26317     {
26318         n.parentNode.removeChild(n);
26319     }
26320 });/**
26321  * @class Roo.htmleditor.FilterKeepChildren
26322  * remove tags but keep children
26323  * @constructor
26324  * Run a new Keep Children Filter
26325  * @param {Object} config Configuration options
26326  */
26327
26328 Roo.htmleditor.FilterKeepChildren = function(cfg)
26329 {
26330     Roo.apply(this, cfg);
26331     if (this.tag === false) {
26332         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26333     }
26334     // hacky?
26335     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26336         this.cleanNamespace = true;
26337     }
26338         
26339     this.walk(cfg.node);
26340 }
26341
26342 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26343 {
26344     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26345   
26346     replaceTag : function(node)
26347     {
26348         // walk children...
26349         //Roo.log(node.tagName);
26350         var ar = Array.from(node.childNodes);
26351         //remove first..
26352         
26353         for (var i = 0; i < ar.length; i++) {
26354             var e = ar[i];
26355             if (e.nodeType == 1) {
26356                 if (
26357                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26358                     || // array and it matches
26359                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
26360                     ||
26361                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26362                     ||
26363                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26364                 ) {
26365                     this.replaceTag(ar[i]); // child is blacklisted as well...
26366                     continue;
26367                 }
26368             }
26369         }  
26370         ar = Array.from(node.childNodes);
26371         for (var i = 0; i < ar.length; i++) {
26372          
26373             node.removeChild(ar[i]);
26374             // what if we need to walk these???
26375             node.parentNode.insertBefore(ar[i], node);
26376             if (this.tag !== false) {
26377                 this.walk(ar[i]);
26378                 
26379             }
26380         }
26381         //Roo.log("REMOVE:" + node.tagName);
26382         node.parentNode.removeChild(node);
26383         return false; // don't walk children
26384         
26385         
26386     }
26387 });/**
26388  * @class Roo.htmleditor.FilterParagraph
26389  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26390  * like on 'push' to remove the <p> tags and replace them with line breaks.
26391  * @constructor
26392  * Run a new Paragraph Filter
26393  * @param {Object} config Configuration options
26394  */
26395
26396 Roo.htmleditor.FilterParagraph = function(cfg)
26397 {
26398     // no need to apply config.
26399     this.walk(cfg.node);
26400 }
26401
26402 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26403 {
26404     
26405      
26406     tag : 'P',
26407     
26408      
26409     replaceTag : function(node)
26410     {
26411         
26412         if (node.childNodes.length == 1 &&
26413             node.childNodes[0].nodeType == 3 &&
26414             node.childNodes[0].textContent.trim().length < 1
26415             ) {
26416             // remove and replace with '<BR>';
26417             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26418             return false; // no need to walk..
26419         }
26420         var ar = Array.from(node.childNodes);
26421         for (var i = 0; i < ar.length; i++) {
26422             node.removeChild(ar[i]);
26423             // what if we need to walk these???
26424             node.parentNode.insertBefore(ar[i], node);
26425         }
26426         // now what about this?
26427         // <p> &nbsp; </p>
26428         
26429         // double BR.
26430         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26431         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26432         node.parentNode.removeChild(node);
26433         
26434         return false;
26435
26436     }
26437     
26438 });/**
26439  * @class Roo.htmleditor.FilterSpan
26440  * filter span's with no attributes out..
26441  * @constructor
26442  * Run a new Span Filter
26443  * @param {Object} config Configuration options
26444  */
26445
26446 Roo.htmleditor.FilterSpan = function(cfg)
26447 {
26448     // no need to apply config.
26449     this.walk(cfg.node);
26450 }
26451
26452 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26453 {
26454      
26455     tag : 'SPAN',
26456      
26457  
26458     replaceTag : function(node)
26459     {
26460         if (node.attributes && node.attributes.length > 0) {
26461             return true; // walk if there are any.
26462         }
26463         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26464         return false;
26465      
26466     }
26467     
26468 });/**
26469  * @class Roo.htmleditor.FilterTableWidth
26470   try and remove table width data - as that frequently messes up other stuff.
26471  * 
26472  *      was cleanTableWidths.
26473  *
26474  * Quite often pasting from word etc.. results in tables with column and widths.
26475  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26476  *
26477  * @constructor
26478  * Run a new Table Filter
26479  * @param {Object} config Configuration options
26480  */
26481
26482 Roo.htmleditor.FilterTableWidth = function(cfg)
26483 {
26484     // no need to apply config.
26485     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26486     this.walk(cfg.node);
26487 }
26488
26489 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26490 {
26491      
26492      
26493     
26494     replaceTag: function(node) {
26495         
26496         
26497       
26498         if (node.hasAttribute('width')) {
26499             node.removeAttribute('width');
26500         }
26501         
26502          
26503         if (node.hasAttribute("style")) {
26504             // pretty basic...
26505             
26506             var styles = node.getAttribute("style").split(";");
26507             var nstyle = [];
26508             Roo.each(styles, function(s) {
26509                 if (!s.match(/:/)) {
26510                     return;
26511                 }
26512                 var kv = s.split(":");
26513                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26514                     return;
26515                 }
26516                 // what ever is left... we allow.
26517                 nstyle.push(s);
26518             });
26519             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26520             if (!nstyle.length) {
26521                 node.removeAttribute('style');
26522             }
26523         }
26524         
26525         return true; // continue doing children..
26526     }
26527 });/**
26528  * @class Roo.htmleditor.FilterWord
26529  * try and clean up all the mess that Word generates.
26530  * 
26531  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
26532  
26533  * @constructor
26534  * Run a new Span Filter
26535  * @param {Object} config Configuration options
26536  */
26537
26538 Roo.htmleditor.FilterWord = function(cfg)
26539 {
26540     // no need to apply config.
26541     this.replaceDocBullets(cfg.node);
26542     
26543     this.replaceAname(cfg.node);
26544     // this is disabled as the removal is done by other filters;
26545    // this.walk(cfg.node);
26546     
26547     
26548 }
26549
26550 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
26551 {
26552     tag: true,
26553      
26554     
26555     /**
26556      * Clean up MS wordisms...
26557      */
26558     replaceTag : function(node)
26559     {
26560          
26561         // no idea what this does - span with text, replaceds with just text.
26562         if(
26563                 node.nodeName == 'SPAN' &&
26564                 !node.hasAttributes() &&
26565                 node.childNodes.length == 1 &&
26566                 node.firstChild.nodeName == "#text"  
26567         ) {
26568             var textNode = node.firstChild;
26569             node.removeChild(textNode);
26570             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26571                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26572             }
26573             node.parentNode.insertBefore(textNode, node);
26574             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26575                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26576             }
26577             
26578             node.parentNode.removeChild(node);
26579             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
26580         }
26581         
26582    
26583         
26584         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26585             node.parentNode.removeChild(node);
26586             return false; // dont do chidlren
26587         }
26588         //Roo.log(node.tagName);
26589         // remove - but keep children..
26590         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26591             //Roo.log('-- removed');
26592             while (node.childNodes.length) {
26593                 var cn = node.childNodes[0];
26594                 node.removeChild(cn);
26595                 node.parentNode.insertBefore(cn, node);
26596                 // move node to parent - and clean it..
26597                 if (cn.nodeType == 1) {
26598                     this.replaceTag(cn);
26599                 }
26600                 
26601             }
26602             node.parentNode.removeChild(node);
26603             /// no need to iterate chidlren = it's got none..
26604             //this.iterateChildren(node, this.cleanWord);
26605             return false; // no need to iterate children.
26606         }
26607         // clean styles
26608         if (node.className.length) {
26609             
26610             var cn = node.className.split(/\W+/);
26611             var cna = [];
26612             Roo.each(cn, function(cls) {
26613                 if (cls.match(/Mso[a-zA-Z]+/)) {
26614                     return;
26615                 }
26616                 cna.push(cls);
26617             });
26618             node.className = cna.length ? cna.join(' ') : '';
26619             if (!cna.length) {
26620                 node.removeAttribute("class");
26621             }
26622         }
26623         
26624         if (node.hasAttribute("lang")) {
26625             node.removeAttribute("lang");
26626         }
26627         
26628         if (node.hasAttribute("style")) {
26629             
26630             var styles = node.getAttribute("style").split(";");
26631             var nstyle = [];
26632             Roo.each(styles, function(s) {
26633                 if (!s.match(/:/)) {
26634                     return;
26635                 }
26636                 var kv = s.split(":");
26637                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26638                     return;
26639                 }
26640                 // what ever is left... we allow.
26641                 nstyle.push(s);
26642             });
26643             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26644             if (!nstyle.length) {
26645                 node.removeAttribute('style');
26646             }
26647         }
26648         return true; // do children
26649         
26650         
26651         
26652     },
26653     
26654     styleToObject: function(node)
26655     {
26656         var styles = (node.getAttribute("style") || '').split(";");
26657         var ret = {};
26658         Roo.each(styles, function(s) {
26659             if (!s.match(/:/)) {
26660                 return;
26661             }
26662             var kv = s.split(":");
26663              
26664             // what ever is left... we allow.
26665             ret[kv[0].trim()] = kv[1];
26666         });
26667         return ret;
26668     },
26669     
26670     
26671     replaceAname : function (doc)
26672     {
26673         // replace all the a/name without..
26674         var aa = Array.from(doc.getElementsByTagName('a'));
26675         for (var i = 0; i  < aa.length; i++) {
26676             var a = aa[i];
26677             if (a.hasAttribute("name")) {
26678                 a.removeAttribute("name");
26679             }
26680             if (a.hasAttribute("href")) {
26681                 continue;
26682             }
26683             // reparent children.
26684             this.removeNodeKeepChildren(a);
26685             
26686         }
26687         
26688         
26689         
26690     },
26691
26692     
26693     
26694     replaceDocBullets : function(doc)
26695     {
26696         // this is a bit odd - but it appears some indents use ql-indent-1
26697          //Roo.log(doc.innerHTML);
26698         
26699         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
26700         for( var i = 0; i < listpara.length; i ++) {
26701             listpara[i].className = "MsoListParagraph";
26702         }
26703         
26704         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
26705         for( var i = 0; i < listpara.length; i ++) {
26706             listpara[i].className = "MsoListParagraph";
26707         }
26708         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
26709         for( var i = 0; i < listpara.length; i ++) {
26710             listpara[i].className = "MsoListParagraph";
26711         }
26712         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
26713         for( var i = 0; i < listpara.length; i ++) {
26714             listpara[i].className = "MsoListParagraph";
26715         }
26716         
26717         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
26718         var htwo =  Array.from(doc.getElementsByTagName('h2'));
26719         for( var i = 0; i < htwo.length; i ++) {
26720             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
26721                 htwo[i].className = "MsoListParagraph";
26722             }
26723         }
26724         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
26725         for( var i = 0; i < listpara.length; i ++) {
26726             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
26727                 listpara[i].className = "MsoListParagraph";
26728             } else {
26729                 listpara[i].className = "MsoNormalx";
26730             }
26731         }
26732        
26733         listpara = doc.getElementsByClassName('MsoListParagraph');
26734         // Roo.log(doc.innerHTML);
26735         
26736         
26737         
26738         while(listpara.length) {
26739             
26740             this.replaceDocBullet(listpara.item(0));
26741         }
26742       
26743     },
26744     
26745      
26746     
26747     replaceDocBullet : function(p)
26748     {
26749         // gather all the siblings.
26750         var ns = p,
26751             parent = p.parentNode,
26752             doc = parent.ownerDocument,
26753             items = [];
26754             
26755         var listtype = 'ul';   
26756         while (ns) {
26757             if (ns.nodeType != 1) {
26758                 ns = ns.nextSibling;
26759                 continue;
26760             }
26761             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
26762                 break;
26763             }
26764             var spans = ns.getElementsByTagName('span');
26765             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
26766                 items.push(ns);
26767                 ns = ns.nextSibling;
26768                 has_list = true;
26769                 if (spans.length && spans[0].hasAttribute('style')) {
26770                     var  style = this.styleToObject(spans[0]);
26771                     if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
26772                         listtype = 'ol';
26773                     }
26774                 }
26775                 
26776                 continue;
26777             }
26778             var spans = ns.getElementsByTagName('span');
26779             if (!spans.length) {
26780                 break;
26781             }
26782             var has_list  = false;
26783             for(var i = 0; i < spans.length; i++) {
26784                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
26785                     has_list = true;
26786                     break;
26787                 }
26788             }
26789             if (!has_list) {
26790                 break;
26791             }
26792             items.push(ns);
26793             ns = ns.nextSibling;
26794             
26795             
26796         }
26797         if (!items.length) {
26798             ns.className = "";
26799             return;
26800         }
26801         
26802         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
26803         parent.insertBefore(ul, p);
26804         var lvl = 0;
26805         var stack = [ ul ];
26806         var last_li = false;
26807         
26808         var margin_to_depth = {};
26809         max_margins = -1;
26810         
26811         items.forEach(function(n, ipos) {
26812             //Roo.log("got innertHMLT=" + n.innerHTML);
26813             
26814             var spans = n.getElementsByTagName('span');
26815             if (!spans.length) {
26816                 //Roo.log("No spans found");
26817                  
26818                 parent.removeChild(n);
26819                 
26820                 
26821                 return; // skip it...
26822             }
26823            
26824                 
26825             var num = 1;
26826             var style = {};
26827             for(var i = 0; i < spans.length; i++) {
26828             
26829                 style = this.styleToObject(spans[i]);
26830                 if (typeof(style['mso-list']) == 'undefined') {
26831                     continue;
26832                 }
26833                 if (listtype == 'ol') {
26834                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
26835                 }
26836                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
26837                 break;
26838             }
26839             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
26840             style = this.styleToObject(n); // mo-list is from the parent node.
26841             if (typeof(style['mso-list']) == 'undefined') {
26842                 //Roo.log("parent is missing level");
26843                   
26844                 parent.removeChild(n);
26845                  
26846                 return;
26847             }
26848             
26849             var margin = style['margin-left'];
26850             if (typeof(margin_to_depth[margin]) == 'undefined') {
26851                 max_margins++;
26852                 margin_to_depth[margin] = max_margins;
26853             }
26854             nlvl = margin_to_depth[margin] ;
26855              
26856             if (nlvl > lvl) {
26857                 //new indent
26858                 var nul = doc.createElement(listtype); // what about number lists...
26859                 if (!last_li) {
26860                     last_li = doc.createElement('li');
26861                     stack[lvl].appendChild(last_li);
26862                 }
26863                 last_li.appendChild(nul);
26864                 stack[nlvl] = nul;
26865                 
26866             }
26867             lvl = nlvl;
26868             
26869             // not starting at 1..
26870             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
26871                 stack[nlvl].setAttribute("start", num);
26872             }
26873             
26874             var nli = stack[nlvl].appendChild(doc.createElement('li'));
26875             last_li = nli;
26876             nli.innerHTML = n.innerHTML;
26877             //Roo.log("innerHTML = " + n.innerHTML);
26878             parent.removeChild(n);
26879             
26880              
26881              
26882             
26883         },this);
26884         
26885         
26886         
26887         
26888     }
26889     
26890     
26891     
26892 });
26893 /**
26894  * @class Roo.htmleditor.FilterStyleToTag
26895  * part of the word stuff... - certain 'styles' should be converted to tags.
26896  * eg.
26897  *   font-weight: bold -> bold
26898  *   ?? super / subscrit etc..
26899  * 
26900  * @constructor
26901 * Run a new style to tag filter.
26902 * @param {Object} config Configuration options
26903  */
26904 Roo.htmleditor.FilterStyleToTag = function(cfg)
26905 {
26906     
26907     this.tags = {
26908         B  : [ 'fontWeight' , 'bold'],
26909         I :  [ 'fontStyle' , 'italic'],
26910         //pre :  [ 'font-style' , 'italic'],
26911         // h1.. h6 ?? font-size?
26912         SUP : [ 'verticalAlign' , 'super' ],
26913         SUB : [ 'verticalAlign' , 'sub' ]
26914         
26915         
26916     };
26917     
26918     Roo.apply(this, cfg);
26919      
26920     
26921     this.walk(cfg.node);
26922     
26923     
26924     
26925 }
26926
26927
26928 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
26929 {
26930     tag: true, // all tags
26931     
26932     tags : false,
26933     
26934     
26935     replaceTag : function(node)
26936     {
26937         
26938         
26939         if (node.getAttribute("style") === null) {
26940             return true;
26941         }
26942         var inject = [];
26943         for (var k in this.tags) {
26944             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
26945                 inject.push(k);
26946                 node.style.removeProperty(this.tags[k][0]);
26947             }
26948         }
26949         if (!inject.length) {
26950             return true; 
26951         }
26952         var cn = Array.from(node.childNodes);
26953         var nn = node;
26954         Roo.each(inject, function(t) {
26955             var nc = node.ownerDocument.createElement(t);
26956             nn.appendChild(nc);
26957             nn = nc;
26958         });
26959         for(var i = 0;i < cn.length;cn++) {
26960             node.removeChild(cn[i]);
26961             nn.appendChild(cn[i]);
26962         }
26963         return true /// iterate thru
26964     }
26965     
26966 })/**
26967  * @class Roo.htmleditor.FilterLongBr
26968  * BR/BR/BR - keep a maximum of 2...
26969  * @constructor
26970  * Run a new Long BR Filter
26971  * @param {Object} config Configuration options
26972  */
26973
26974 Roo.htmleditor.FilterLongBr = function(cfg)
26975 {
26976     // no need to apply config.
26977     this.walk(cfg.node);
26978 }
26979
26980 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
26981 {
26982     
26983      
26984     tag : 'BR',
26985     
26986      
26987     replaceTag : function(node)
26988     {
26989         
26990         var ps = node.nextSibling;
26991         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26992             ps = ps.nextSibling;
26993         }
26994         
26995         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
26996             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
26997             return false;
26998         }
26999         
27000         if (!ps || ps.nodeType != 1) {
27001             return false;
27002         }
27003         
27004         if (!ps || ps.tagName != 'BR') {
27005            
27006             return false;
27007         }
27008         
27009         
27010         
27011         
27012         
27013         if (!node.previousSibling) {
27014             return false;
27015         }
27016         var ps = node.previousSibling;
27017         
27018         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27019             ps = ps.previousSibling;
27020         }
27021         if (!ps || ps.nodeType != 1) {
27022             return false;
27023         }
27024         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27025         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27026             return false;
27027         }
27028         
27029         node.parentNode.removeChild(node); // remove me...
27030         
27031         return false; // no need to do children
27032
27033     }
27034     
27035 }); 
27036
27037 /**
27038  * @class Roo.htmleditor.FilterBlock
27039  * removes id / data-block and contenteditable that are associated with blocks
27040  * usage should be done on a cloned copy of the dom
27041  * @constructor
27042 * Run a new Attribute Filter { node : xxxx }}
27043 * @param {Object} config Configuration options
27044  */
27045 Roo.htmleditor.FilterBlock = function(cfg)
27046 {
27047     Roo.apply(this, cfg);
27048     var qa = cfg.node.querySelectorAll;
27049     this.removeAttributes('data-block');
27050     this.removeAttributes('contenteditable');
27051     this.removeAttributes('id');
27052     
27053 }
27054
27055 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27056 {
27057     node: true, // all tags
27058      
27059      
27060     removeAttributes : function(attr)
27061     {
27062         var ar = this.node.querySelectorAll('*[' + attr + ']');
27063         for (var i =0;i<ar.length;i++) {
27064             ar[i].removeAttribute(attr);
27065         }
27066     }
27067         
27068         
27069         
27070     
27071 });
27072 /***
27073  * This is based loosely on tinymce 
27074  * @class Roo.htmleditor.TidySerializer
27075  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27076  * @constructor
27077  * @method Serializer
27078  * @param {Object} settings Name/value settings object.
27079  */
27080
27081
27082 Roo.htmleditor.TidySerializer = function(settings)
27083 {
27084     Roo.apply(this, settings);
27085     
27086     this.writer = new Roo.htmleditor.TidyWriter(settings);
27087     
27088     
27089
27090 };
27091 Roo.htmleditor.TidySerializer.prototype = {
27092     
27093     /**
27094      * @param {boolean} inner do the inner of the node.
27095      */
27096     inner : false,
27097     
27098     writer : false,
27099     
27100     /**
27101     * Serializes the specified node into a string.
27102     *
27103     * @example
27104     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27105     * @method serialize
27106     * @param {DomElement} node Node instance to serialize.
27107     * @return {String} String with HTML based on DOM tree.
27108     */
27109     serialize : function(node) {
27110         
27111         // = settings.validate;
27112         var writer = this.writer;
27113         var self  = this;
27114         this.handlers = {
27115             // #text
27116             3: function(node) {
27117                 
27118                 writer.text(node.nodeValue, node);
27119             },
27120             // #comment
27121             8: function(node) {
27122                 writer.comment(node.nodeValue);
27123             },
27124             // Processing instruction
27125             7: function(node) {
27126                 writer.pi(node.name, node.nodeValue);
27127             },
27128             // Doctype
27129             10: function(node) {
27130                 writer.doctype(node.nodeValue);
27131             },
27132             // CDATA
27133             4: function(node) {
27134                 writer.cdata(node.nodeValue);
27135             },
27136             // Document fragment
27137             11: function(node) {
27138                 node = node.firstChild;
27139                 if (!node) {
27140                     return;
27141                 }
27142                 while(node) {
27143                     self.walk(node);
27144                     node = node.nextSibling
27145                 }
27146             }
27147         };
27148         writer.reset();
27149         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27150         return writer.getContent();
27151     },
27152
27153     walk: function(node)
27154     {
27155         var attrName, attrValue, sortedAttrs, i, l, elementRule,
27156             handler = this.handlers[node.nodeType];
27157             
27158         if (handler) {
27159             handler(node);
27160             return;
27161         }
27162     
27163         var name = node.nodeName;
27164         var isEmpty = node.childNodes.length < 1;
27165       
27166         var writer = this.writer;
27167         var attrs = node.attributes;
27168         // Sort attributes
27169         
27170         writer.start(node.nodeName, attrs, isEmpty, node);
27171         if (isEmpty) {
27172             return;
27173         }
27174         node = node.firstChild;
27175         if (!node) {
27176             writer.end(name);
27177             return;
27178         }
27179         while (node) {
27180             this.walk(node);
27181             node = node.nextSibling;
27182         }
27183         writer.end(name);
27184         
27185     
27186     }
27187     // Serialize element and treat all non elements as fragments
27188    
27189 }; 
27190
27191 /***
27192  * This is based loosely on tinymce 
27193  * @class Roo.htmleditor.TidyWriter
27194  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27195  *
27196  * Known issues?
27197  * - not tested much with 'PRE' formated elements.
27198  * 
27199  *
27200  *
27201  */
27202
27203 Roo.htmleditor.TidyWriter = function(settings)
27204 {
27205     
27206     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27207     Roo.apply(this, settings);
27208     this.html = [];
27209     this.state = [];
27210      
27211     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27212   
27213 }
27214 Roo.htmleditor.TidyWriter.prototype = {
27215
27216  
27217     state : false,
27218     
27219     indent :  '  ',
27220     
27221     // part of state...
27222     indentstr : '',
27223     in_pre: false,
27224     in_inline : false,
27225     last_inline : false,
27226     encode : false,
27227      
27228     
27229             /**
27230     * Writes the a start element such as <p id="a">.
27231     *
27232     * @method start
27233     * @param {String} name Name of the element.
27234     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27235     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27236     */
27237     start: function(name, attrs, empty, node)
27238     {
27239         var i, l, attr, value;
27240         
27241         // there are some situations where adding line break && indentation will not work. will not work.
27242         // <span / b / i ... formating?
27243         
27244         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27245         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27246         
27247         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27248         
27249         var add_lb = name == 'BR' ? false : in_inline;
27250         
27251         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27252             i_inline = false;
27253         }
27254
27255         var indentstr =  this.indentstr;
27256         
27257         // e_inline = elements that can be inline, but still allow \n before and after?
27258         // only 'BR' ??? any others?
27259         
27260         // ADD LINE BEFORE tage
27261         if (!this.in_pre) {
27262             if (in_inline) {
27263                 //code
27264                 if (name == 'BR') {
27265                     this.addLine();
27266                 } else if (this.lastElementEndsWS()) {
27267                     this.addLine();
27268                 } else{
27269                     // otherwise - no new line. (and dont indent.)
27270                     indentstr = '';
27271                 }
27272                 
27273             } else {
27274                 this.addLine();
27275             }
27276         } else {
27277             indentstr = '';
27278         }
27279         
27280         this.html.push(indentstr + '<', name.toLowerCase());
27281         
27282         if (attrs) {
27283             for (i = 0, l = attrs.length; i < l; i++) {
27284                 attr = attrs[i];
27285                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27286             }
27287         }
27288      
27289         if (empty) {
27290             if (is_short) {
27291                 this.html[this.html.length] = '/>';
27292             } else {
27293                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27294             }
27295             var e_inline = name == 'BR' ? false : this.in_inline;
27296             
27297             if (!e_inline && !this.in_pre) {
27298                 this.addLine();
27299             }
27300             return;
27301         
27302         }
27303         // not empty..
27304         this.html[this.html.length] = '>';
27305         
27306         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27307         /*
27308         if (!in_inline && !in_pre) {
27309             var cn = node.firstChild;
27310             while(cn) {
27311                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27312                     in_inline = true
27313                     break;
27314                 }
27315                 cn = cn.nextSibling;
27316             }
27317              
27318         }
27319         */
27320         
27321         
27322         this.pushState({
27323             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
27324             in_pre : in_pre,
27325             in_inline :  in_inline
27326         });
27327         // add a line after if we are not in a
27328         
27329         if (!in_inline && !in_pre) {
27330             this.addLine();
27331         }
27332         
27333             
27334          
27335         
27336     },
27337     
27338     lastElementEndsWS : function()
27339     {
27340         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27341         if (value === false) {
27342             return true;
27343         }
27344         return value.match(/\s+$/);
27345         
27346     },
27347     
27348     /**
27349      * Writes the a end element such as </p>.
27350      *
27351      * @method end
27352      * @param {String} name Name of the element.
27353      */
27354     end: function(name) {
27355         var value;
27356         this.popState();
27357         var indentstr = '';
27358         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27359         
27360         if (!this.in_pre && !in_inline) {
27361             this.addLine();
27362             indentstr  = this.indentstr;
27363         }
27364         this.html.push(indentstr + '</', name.toLowerCase(), '>');
27365         this.last_inline = in_inline;
27366         
27367         // pop the indent state..
27368     },
27369     /**
27370      * Writes a text node.
27371      *
27372      * In pre - we should not mess with the contents.
27373      * 
27374      *
27375      * @method text
27376      * @param {String} text String to write out.
27377      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27378      */
27379     text: function(in_text, node)
27380     {
27381         // if not in whitespace critical
27382         if (in_text.length < 1) {
27383             return;
27384         }
27385         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27386         
27387         if (this.in_pre) {
27388             this.html[this.html.length] =  text;
27389             return;   
27390         }
27391         
27392         if (this.in_inline) {
27393             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27394             if (text != ' ') {
27395                 text = text.replace(/\s+/,' ');  // all white space to single white space
27396                 
27397                     
27398                 // if next tag is '<BR>', then we can trim right..
27399                 if (node.nextSibling &&
27400                     node.nextSibling.nodeType == 1 &&
27401                     node.nextSibling.nodeName == 'BR' )
27402                 {
27403                     text = text.replace(/\s+$/g,'');
27404                 }
27405                 // if previous tag was a BR, we can also trim..
27406                 if (node.previousSibling &&
27407                     node.previousSibling.nodeType == 1 &&
27408                     node.previousSibling.nodeName == 'BR' )
27409                 {
27410                     text = this.indentstr +  text.replace(/^\s+/g,'');
27411                 }
27412                 if (text.match(/\n/)) {
27413                     text = text.replace(
27414                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27415                     );
27416                     // remoeve the last whitespace / line break.
27417                     text = text.replace(/\n\s+$/,'');
27418                 }
27419                 // repace long lines
27420                 
27421             }
27422              
27423             this.html[this.html.length] =  text;
27424             return;   
27425         }
27426         // see if previous element was a inline element.
27427         var indentstr = this.indentstr;
27428    
27429         text = text.replace(/\s+/g," "); // all whitespace into single white space.
27430         
27431         // should trim left?
27432         if (node.previousSibling &&
27433             node.previousSibling.nodeType == 1 &&
27434             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27435         {
27436             indentstr = '';
27437             
27438         } else {
27439             this.addLine();
27440             text = text.replace(/^\s+/,''); // trim left
27441           
27442         }
27443         // should trim right?
27444         if (node.nextSibling &&
27445             node.nextSibling.nodeType == 1 &&
27446             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27447         {
27448           // noop
27449             
27450         }  else {
27451             text = text.replace(/\s+$/,''); // trim right
27452         }
27453          
27454               
27455         
27456         
27457         
27458         if (text.length < 1) {
27459             return;
27460         }
27461         if (!text.match(/\n/)) {
27462             this.html.push(indentstr + text);
27463             return;
27464         }
27465         
27466         text = this.indentstr + text.replace(
27467             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27468         );
27469         // remoeve the last whitespace / line break.
27470         text = text.replace(/\s+$/,''); 
27471         
27472         this.html.push(text);
27473         
27474         // split and indent..
27475         
27476         
27477     },
27478     /**
27479      * Writes a cdata node such as <![CDATA[data]]>.
27480      *
27481      * @method cdata
27482      * @param {String} text String to write out inside the cdata.
27483      */
27484     cdata: function(text) {
27485         this.html.push('<![CDATA[', text, ']]>');
27486     },
27487     /**
27488     * Writes a comment node such as <!-- Comment -->.
27489     *
27490     * @method cdata
27491     * @param {String} text String to write out inside the comment.
27492     */
27493    comment: function(text) {
27494        this.html.push('<!--', text, '-->');
27495    },
27496     /**
27497      * Writes a PI node such as <?xml attr="value" ?>.
27498      *
27499      * @method pi
27500      * @param {String} name Name of the pi.
27501      * @param {String} text String to write out inside the pi.
27502      */
27503     pi: function(name, text) {
27504         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
27505         this.indent != '' && this.html.push('\n');
27506     },
27507     /**
27508      * Writes a doctype node such as <!DOCTYPE data>.
27509      *
27510      * @method doctype
27511      * @param {String} text String to write out inside the doctype.
27512      */
27513     doctype: function(text) {
27514         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
27515     },
27516     /**
27517      * Resets the internal buffer if one wants to reuse the writer.
27518      *
27519      * @method reset
27520      */
27521     reset: function() {
27522         this.html.length = 0;
27523         this.state = [];
27524         this.pushState({
27525             indentstr : '',
27526             in_pre : false, 
27527             in_inline : false
27528         })
27529     },
27530     /**
27531      * Returns the contents that got serialized.
27532      *
27533      * @method getContent
27534      * @return {String} HTML contents that got written down.
27535      */
27536     getContent: function() {
27537         return this.html.join('').replace(/\n$/, '');
27538     },
27539     
27540     pushState : function(cfg)
27541     {
27542         this.state.push(cfg);
27543         Roo.apply(this, cfg);
27544     },
27545     
27546     popState : function()
27547     {
27548         if (this.state.length < 1) {
27549             return; // nothing to push
27550         }
27551         var cfg = {
27552             in_pre: false,
27553             indentstr : ''
27554         };
27555         this.state.pop();
27556         if (this.state.length > 0) {
27557             cfg = this.state[this.state.length-1]; 
27558         }
27559         Roo.apply(this, cfg);
27560     },
27561     
27562     addLine: function()
27563     {
27564         if (this.html.length < 1) {
27565             return;
27566         }
27567         
27568         
27569         var value = this.html[this.html.length - 1];
27570         if (value.length > 0 && '\n' !== value) {
27571             this.html.push('\n');
27572         }
27573     }
27574     
27575     
27576 //'pre script noscript style textarea video audio iframe object code'
27577 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
27578 // inline 
27579 };
27580
27581 Roo.htmleditor.TidyWriter.inline_elements = [
27582         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
27583         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
27584 ];
27585 Roo.htmleditor.TidyWriter.shortend_elements = [
27586     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
27587     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
27588 ];
27589
27590 Roo.htmleditor.TidyWriter.whitespace_elements = [
27591     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
27592 ];/***
27593  * This is based loosely on tinymce 
27594  * @class Roo.htmleditor.TidyEntities
27595  * @static
27596  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27597  *
27598  * Not 100% sure this is actually used or needed.
27599  */
27600
27601 Roo.htmleditor.TidyEntities = {
27602     
27603     /**
27604      * initialize data..
27605      */
27606     init : function (){
27607      
27608         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
27609        
27610     },
27611
27612
27613     buildEntitiesLookup: function(items, radix) {
27614         var i, chr, entity, lookup = {};
27615         if (!items) {
27616             return {};
27617         }
27618         items = typeof(items) == 'string' ? items.split(',') : items;
27619         radix = radix || 10;
27620         // Build entities lookup table
27621         for (i = 0; i < items.length; i += 2) {
27622             chr = String.fromCharCode(parseInt(items[i], radix));
27623             // Only add non base entities
27624             if (!this.baseEntities[chr]) {
27625                 entity = '&' + items[i + 1] + ';';
27626                 lookup[chr] = entity;
27627                 lookup[entity] = chr;
27628             }
27629         }
27630         return lookup;
27631         
27632     },
27633     
27634     asciiMap : {
27635             128: '€',
27636             130: '‚',
27637             131: 'ƒ',
27638             132: '„',
27639             133: '…',
27640             134: '†',
27641             135: '‡',
27642             136: 'ˆ',
27643             137: '‰',
27644             138: 'Š',
27645             139: '‹',
27646             140: 'Œ',
27647             142: 'Ž',
27648             145: '‘',
27649             146: '’',
27650             147: '“',
27651             148: '”',
27652             149: '•',
27653             150: '–',
27654             151: '—',
27655             152: '˜',
27656             153: '™',
27657             154: 'š',
27658             155: '›',
27659             156: 'œ',
27660             158: 'ž',
27661             159: 'Ÿ'
27662     },
27663     // Raw entities
27664     baseEntities : {
27665         '"': '&quot;',
27666         // Needs to be escaped since the YUI compressor would otherwise break the code
27667         '\'': '&#39;',
27668         '<': '&lt;',
27669         '>': '&gt;',
27670         '&': '&amp;',
27671         '`': '&#96;'
27672     },
27673     // Reverse lookup table for raw entities
27674     reverseEntities : {
27675         '&lt;': '<',
27676         '&gt;': '>',
27677         '&amp;': '&',
27678         '&quot;': '"',
27679         '&apos;': '\''
27680     },
27681     
27682     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
27683     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
27684     rawCharsRegExp : /[<>&\"\']/g,
27685     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
27686     namedEntities  : false,
27687     namedEntitiesData : [ 
27688         '50',
27689         'nbsp',
27690         '51',
27691         'iexcl',
27692         '52',
27693         'cent',
27694         '53',
27695         'pound',
27696         '54',
27697         'curren',
27698         '55',
27699         'yen',
27700         '56',
27701         'brvbar',
27702         '57',
27703         'sect',
27704         '58',
27705         'uml',
27706         '59',
27707         'copy',
27708         '5a',
27709         'ordf',
27710         '5b',
27711         'laquo',
27712         '5c',
27713         'not',
27714         '5d',
27715         'shy',
27716         '5e',
27717         'reg',
27718         '5f',
27719         'macr',
27720         '5g',
27721         'deg',
27722         '5h',
27723         'plusmn',
27724         '5i',
27725         'sup2',
27726         '5j',
27727         'sup3',
27728         '5k',
27729         'acute',
27730         '5l',
27731         'micro',
27732         '5m',
27733         'para',
27734         '5n',
27735         'middot',
27736         '5o',
27737         'cedil',
27738         '5p',
27739         'sup1',
27740         '5q',
27741         'ordm',
27742         '5r',
27743         'raquo',
27744         '5s',
27745         'frac14',
27746         '5t',
27747         'frac12',
27748         '5u',
27749         'frac34',
27750         '5v',
27751         'iquest',
27752         '60',
27753         'Agrave',
27754         '61',
27755         'Aacute',
27756         '62',
27757         'Acirc',
27758         '63',
27759         'Atilde',
27760         '64',
27761         'Auml',
27762         '65',
27763         'Aring',
27764         '66',
27765         'AElig',
27766         '67',
27767         'Ccedil',
27768         '68',
27769         'Egrave',
27770         '69',
27771         'Eacute',
27772         '6a',
27773         'Ecirc',
27774         '6b',
27775         'Euml',
27776         '6c',
27777         'Igrave',
27778         '6d',
27779         'Iacute',
27780         '6e',
27781         'Icirc',
27782         '6f',
27783         'Iuml',
27784         '6g',
27785         'ETH',
27786         '6h',
27787         'Ntilde',
27788         '6i',
27789         'Ograve',
27790         '6j',
27791         'Oacute',
27792         '6k',
27793         'Ocirc',
27794         '6l',
27795         'Otilde',
27796         '6m',
27797         'Ouml',
27798         '6n',
27799         'times',
27800         '6o',
27801         'Oslash',
27802         '6p',
27803         'Ugrave',
27804         '6q',
27805         'Uacute',
27806         '6r',
27807         'Ucirc',
27808         '6s',
27809         'Uuml',
27810         '6t',
27811         'Yacute',
27812         '6u',
27813         'THORN',
27814         '6v',
27815         'szlig',
27816         '70',
27817         'agrave',
27818         '71',
27819         'aacute',
27820         '72',
27821         'acirc',
27822         '73',
27823         'atilde',
27824         '74',
27825         'auml',
27826         '75',
27827         'aring',
27828         '76',
27829         'aelig',
27830         '77',
27831         'ccedil',
27832         '78',
27833         'egrave',
27834         '79',
27835         'eacute',
27836         '7a',
27837         'ecirc',
27838         '7b',
27839         'euml',
27840         '7c',
27841         'igrave',
27842         '7d',
27843         'iacute',
27844         '7e',
27845         'icirc',
27846         '7f',
27847         'iuml',
27848         '7g',
27849         'eth',
27850         '7h',
27851         'ntilde',
27852         '7i',
27853         'ograve',
27854         '7j',
27855         'oacute',
27856         '7k',
27857         'ocirc',
27858         '7l',
27859         'otilde',
27860         '7m',
27861         'ouml',
27862         '7n',
27863         'divide',
27864         '7o',
27865         'oslash',
27866         '7p',
27867         'ugrave',
27868         '7q',
27869         'uacute',
27870         '7r',
27871         'ucirc',
27872         '7s',
27873         'uuml',
27874         '7t',
27875         'yacute',
27876         '7u',
27877         'thorn',
27878         '7v',
27879         'yuml',
27880         'ci',
27881         'fnof',
27882         'sh',
27883         'Alpha',
27884         'si',
27885         'Beta',
27886         'sj',
27887         'Gamma',
27888         'sk',
27889         'Delta',
27890         'sl',
27891         'Epsilon',
27892         'sm',
27893         'Zeta',
27894         'sn',
27895         'Eta',
27896         'so',
27897         'Theta',
27898         'sp',
27899         'Iota',
27900         'sq',
27901         'Kappa',
27902         'sr',
27903         'Lambda',
27904         'ss',
27905         'Mu',
27906         'st',
27907         'Nu',
27908         'su',
27909         'Xi',
27910         'sv',
27911         'Omicron',
27912         't0',
27913         'Pi',
27914         't1',
27915         'Rho',
27916         't3',
27917         'Sigma',
27918         't4',
27919         'Tau',
27920         't5',
27921         'Upsilon',
27922         't6',
27923         'Phi',
27924         't7',
27925         'Chi',
27926         't8',
27927         'Psi',
27928         't9',
27929         'Omega',
27930         'th',
27931         'alpha',
27932         'ti',
27933         'beta',
27934         'tj',
27935         'gamma',
27936         'tk',
27937         'delta',
27938         'tl',
27939         'epsilon',
27940         'tm',
27941         'zeta',
27942         'tn',
27943         'eta',
27944         'to',
27945         'theta',
27946         'tp',
27947         'iota',
27948         'tq',
27949         'kappa',
27950         'tr',
27951         'lambda',
27952         'ts',
27953         'mu',
27954         'tt',
27955         'nu',
27956         'tu',
27957         'xi',
27958         'tv',
27959         'omicron',
27960         'u0',
27961         'pi',
27962         'u1',
27963         'rho',
27964         'u2',
27965         'sigmaf',
27966         'u3',
27967         'sigma',
27968         'u4',
27969         'tau',
27970         'u5',
27971         'upsilon',
27972         'u6',
27973         'phi',
27974         'u7',
27975         'chi',
27976         'u8',
27977         'psi',
27978         'u9',
27979         'omega',
27980         'uh',
27981         'thetasym',
27982         'ui',
27983         'upsih',
27984         'um',
27985         'piv',
27986         '812',
27987         'bull',
27988         '816',
27989         'hellip',
27990         '81i',
27991         'prime',
27992         '81j',
27993         'Prime',
27994         '81u',
27995         'oline',
27996         '824',
27997         'frasl',
27998         '88o',
27999         'weierp',
28000         '88h',
28001         'image',
28002         '88s',
28003         'real',
28004         '892',
28005         'trade',
28006         '89l',
28007         'alefsym',
28008         '8cg',
28009         'larr',
28010         '8ch',
28011         'uarr',
28012         '8ci',
28013         'rarr',
28014         '8cj',
28015         'darr',
28016         '8ck',
28017         'harr',
28018         '8dl',
28019         'crarr',
28020         '8eg',
28021         'lArr',
28022         '8eh',
28023         'uArr',
28024         '8ei',
28025         'rArr',
28026         '8ej',
28027         'dArr',
28028         '8ek',
28029         'hArr',
28030         '8g0',
28031         'forall',
28032         '8g2',
28033         'part',
28034         '8g3',
28035         'exist',
28036         '8g5',
28037         'empty',
28038         '8g7',
28039         'nabla',
28040         '8g8',
28041         'isin',
28042         '8g9',
28043         'notin',
28044         '8gb',
28045         'ni',
28046         '8gf',
28047         'prod',
28048         '8gh',
28049         'sum',
28050         '8gi',
28051         'minus',
28052         '8gn',
28053         'lowast',
28054         '8gq',
28055         'radic',
28056         '8gt',
28057         'prop',
28058         '8gu',
28059         'infin',
28060         '8h0',
28061         'ang',
28062         '8h7',
28063         'and',
28064         '8h8',
28065         'or',
28066         '8h9',
28067         'cap',
28068         '8ha',
28069         'cup',
28070         '8hb',
28071         'int',
28072         '8hk',
28073         'there4',
28074         '8hs',
28075         'sim',
28076         '8i5',
28077         'cong',
28078         '8i8',
28079         'asymp',
28080         '8j0',
28081         'ne',
28082         '8j1',
28083         'equiv',
28084         '8j4',
28085         'le',
28086         '8j5',
28087         'ge',
28088         '8k2',
28089         'sub',
28090         '8k3',
28091         'sup',
28092         '8k4',
28093         'nsub',
28094         '8k6',
28095         'sube',
28096         '8k7',
28097         'supe',
28098         '8kl',
28099         'oplus',
28100         '8kn',
28101         'otimes',
28102         '8l5',
28103         'perp',
28104         '8m5',
28105         'sdot',
28106         '8o8',
28107         'lceil',
28108         '8o9',
28109         'rceil',
28110         '8oa',
28111         'lfloor',
28112         '8ob',
28113         'rfloor',
28114         '8p9',
28115         'lang',
28116         '8pa',
28117         'rang',
28118         '9ea',
28119         'loz',
28120         '9j0',
28121         'spades',
28122         '9j3',
28123         'clubs',
28124         '9j5',
28125         'hearts',
28126         '9j6',
28127         'diams',
28128         'ai',
28129         'OElig',
28130         'aj',
28131         'oelig',
28132         'b0',
28133         'Scaron',
28134         'b1',
28135         'scaron',
28136         'bo',
28137         'Yuml',
28138         'm6',
28139         'circ',
28140         'ms',
28141         'tilde',
28142         '802',
28143         'ensp',
28144         '803',
28145         'emsp',
28146         '809',
28147         'thinsp',
28148         '80c',
28149         'zwnj',
28150         '80d',
28151         'zwj',
28152         '80e',
28153         'lrm',
28154         '80f',
28155         'rlm',
28156         '80j',
28157         'ndash',
28158         '80k',
28159         'mdash',
28160         '80o',
28161         'lsquo',
28162         '80p',
28163         'rsquo',
28164         '80q',
28165         'sbquo',
28166         '80s',
28167         'ldquo',
28168         '80t',
28169         'rdquo',
28170         '80u',
28171         'bdquo',
28172         '810',
28173         'dagger',
28174         '811',
28175         'Dagger',
28176         '81g',
28177         'permil',
28178         '81p',
28179         'lsaquo',
28180         '81q',
28181         'rsaquo',
28182         '85c',
28183         'euro'
28184     ],
28185
28186          
28187     /**
28188      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28189      *
28190      * @method encodeRaw
28191      * @param {String} text Text to encode.
28192      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28193      * @return {String} Entity encoded text.
28194      */
28195     encodeRaw: function(text, attr)
28196     {
28197         var t = this;
28198         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28199             return t.baseEntities[chr] || chr;
28200         });
28201     },
28202     /**
28203      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28204      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28205      * and is exposed as the DOMUtils.encode function.
28206      *
28207      * @method encodeAllRaw
28208      * @param {String} text Text to encode.
28209      * @return {String} Entity encoded text.
28210      */
28211     encodeAllRaw: function(text) {
28212         var t = this;
28213         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28214             return t.baseEntities[chr] || chr;
28215         });
28216     },
28217     /**
28218      * Encodes the specified string using numeric entities. The core entities will be
28219      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28220      *
28221      * @method encodeNumeric
28222      * @param {String} text Text to encode.
28223      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28224      * @return {String} Entity encoded text.
28225      */
28226     encodeNumeric: function(text, attr) {
28227         var t = this;
28228         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28229             // Multi byte sequence convert it to a single entity
28230             if (chr.length > 1) {
28231                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28232             }
28233             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28234         });
28235     },
28236     /**
28237      * Encodes the specified string using named entities. The core entities will be encoded
28238      * as named ones but all non lower ascii characters will be encoded into named entities.
28239      *
28240      * @method encodeNamed
28241      * @param {String} text Text to encode.
28242      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28243      * @param {Object} entities Optional parameter with entities to use.
28244      * @return {String} Entity encoded text.
28245      */
28246     encodeNamed: function(text, attr, entities) {
28247         var t = this;
28248         entities = entities || this.namedEntities;
28249         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28250             return t.baseEntities[chr] || entities[chr] || chr;
28251         });
28252     },
28253     /**
28254      * Returns an encode function based on the name(s) and it's optional entities.
28255      *
28256      * @method getEncodeFunc
28257      * @param {String} name Comma separated list of encoders for example named,numeric.
28258      * @param {String} entities Optional parameter with entities to use instead of the built in set.
28259      * @return {function} Encode function to be used.
28260      */
28261     getEncodeFunc: function(name, entities) {
28262         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28263         var t = this;
28264         function encodeNamedAndNumeric(text, attr) {
28265             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28266                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28267             });
28268         }
28269
28270         function encodeCustomNamed(text, attr) {
28271             return t.encodeNamed(text, attr, entities);
28272         }
28273         // Replace + with , to be compatible with previous TinyMCE versions
28274         name = this.makeMap(name.replace(/\+/g, ','));
28275         // Named and numeric encoder
28276         if (name.named && name.numeric) {
28277             return this.encodeNamedAndNumeric;
28278         }
28279         // Named encoder
28280         if (name.named) {
28281             // Custom names
28282             if (entities) {
28283                 return encodeCustomNamed;
28284             }
28285             return this.encodeNamed;
28286         }
28287         // Numeric
28288         if (name.numeric) {
28289             return this.encodeNumeric;
28290         }
28291         // Raw encoder
28292         return this.encodeRaw;
28293     },
28294     /**
28295      * Decodes the specified string, this will replace entities with raw UTF characters.
28296      *
28297      * @method decode
28298      * @param {String} text Text to entity decode.
28299      * @return {String} Entity decoded string.
28300      */
28301     decode: function(text)
28302     {
28303         var  t = this;
28304         return text.replace(this.entityRegExp, function(all, numeric) {
28305             if (numeric) {
28306                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28307                 // Support upper UTF
28308                 if (numeric > 65535) {
28309                     numeric -= 65536;
28310                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28311                 }
28312                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28313             }
28314             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28315         });
28316     },
28317     nativeDecode : function (text) {
28318         return text;
28319     },
28320     makeMap : function (items, delim, map) {
28321                 var i;
28322                 items = items || [];
28323                 delim = delim || ',';
28324                 if (typeof items == "string") {
28325                         items = items.split(delim);
28326                 }
28327                 map = map || {};
28328                 i = items.length;
28329                 while (i--) {
28330                         map[items[i]] = {};
28331                 }
28332                 return map;
28333         }
28334 };
28335     
28336     
28337     
28338 Roo.htmleditor.TidyEntities.init();
28339 /**
28340  * @class Roo.htmleditor.KeyEnter
28341  * Handle Enter press..
28342  * @cfg {Roo.HtmlEditorCore} core the editor.
28343  * @constructor
28344  * Create a new Filter.
28345  * @param {Object} config Configuration options
28346  */
28347
28348
28349
28350
28351
28352 Roo.htmleditor.KeyEnter = function(cfg) {
28353     Roo.apply(this, cfg);
28354     // this does not actually call walk as it's really just a abstract class
28355  
28356     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28357 }
28358
28359 //Roo.htmleditor.KeyEnter.i = 0;
28360
28361
28362 Roo.htmleditor.KeyEnter.prototype = {
28363     
28364     core : false,
28365     
28366     keypress : function(e)
28367     {
28368         if (e.charCode != 13 && e.charCode != 10) {
28369             Roo.log([e.charCode,e]);
28370             return true;
28371         }
28372         e.preventDefault();
28373         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28374         var doc = this.core.doc;
28375           //add a new line
28376        
28377     
28378         var sel = this.core.getSelection();
28379         var range = sel.getRangeAt(0);
28380         var n = range.commonAncestorContainer;
28381         var pc = range.closest([ 'ol', 'ul']);
28382         var pli = range.closest('li');
28383         if (!pc || e.ctrlKey) {
28384             // on it list, or ctrl pressed.
28385             if (!e.ctrlKey) {
28386                 sel.insertNode('br', 'after'); 
28387             } else {
28388                 // only do this if we have ctrl key..
28389                 var br = doc.createElement('br');
28390                 br.className = 'clear';
28391                 br.setAttribute('style', 'clear: both');
28392                 sel.insertNode(br, 'after'); 
28393             }
28394             
28395          
28396             this.core.undoManager.addEvent();
28397             this.core.fireEditorEvent(e);
28398             return false;
28399         }
28400         
28401         // deal with <li> insetion
28402         if (pli.innerText.trim() == '' &&
28403             pli.previousSibling &&
28404             pli.previousSibling.nodeName == 'LI' &&
28405             pli.previousSibling.innerText.trim() ==  '') {
28406             pli.parentNode.removeChild(pli.previousSibling);
28407             sel.cursorAfter(pc);
28408             this.core.undoManager.addEvent();
28409             this.core.fireEditorEvent(e);
28410             return false;
28411         }
28412     
28413         var li = doc.createElement('LI');
28414         li.innerHTML = '&nbsp;';
28415         if (!pli || !pli.firstSibling) {
28416             pc.appendChild(li);
28417         } else {
28418             pli.parentNode.insertBefore(li, pli.firstSibling);
28419         }
28420         sel.cursorText (li.firstChild);
28421       
28422         this.core.undoManager.addEvent();
28423         this.core.fireEditorEvent(e);
28424
28425         return false;
28426         
28427     
28428         
28429         
28430          
28431     }
28432 };
28433      
28434 /**
28435  * @class Roo.htmleditor.Block
28436  * Base class for html editor blocks - do not use it directly .. extend it..
28437  * @cfg {DomElement} node The node to apply stuff to.
28438  * @cfg {String} friendly_name the name that appears in the context bar about this block
28439  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28440  
28441  * @constructor
28442  * Create a new Filter.
28443  * @param {Object} config Configuration options
28444  */
28445
28446 Roo.htmleditor.Block  = function(cfg)
28447 {
28448     // do nothing .. should not be called really.
28449 }
28450 /**
28451  * factory method to get the block from an element (using cache if necessary)
28452  * @static
28453  * @param {HtmlElement} the dom element
28454  */
28455 Roo.htmleditor.Block.factory = function(node)
28456 {
28457     var cc = Roo.htmleditor.Block.cache;
28458     var id = Roo.get(node).id;
28459     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28460         Roo.htmleditor.Block.cache[id].readElement(node);
28461         return Roo.htmleditor.Block.cache[id];
28462     }
28463     var db  = node.getAttribute('data-block');
28464     if (!db) {
28465         db = node.nodeName.toLowerCase().toUpperCaseFirst();
28466     }
28467     var cls = Roo.htmleditor['Block' + db];
28468     if (typeof(cls) == 'undefined') {
28469         //Roo.log(node.getAttribute('data-block'));
28470         Roo.log("OOps missing block : " + 'Block' + db);
28471         return false;
28472     }
28473     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
28474     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
28475 };
28476
28477 /**
28478  * initalize all Elements from content that are 'blockable'
28479  * @static
28480  * @param the body element
28481  */
28482 Roo.htmleditor.Block.initAll = function(body, type)
28483 {
28484     if (typeof(type) == 'undefined') {
28485         var ia = Roo.htmleditor.Block.initAll;
28486         ia(body,'table');
28487         ia(body,'td');
28488         ia(body,'figure');
28489         return;
28490     }
28491     Roo.each(Roo.get(body).query(type), function(e) {
28492         Roo.htmleditor.Block.factory(e);    
28493     },this);
28494 };
28495 // question goes here... do we need to clear out this cache sometimes?
28496 // or show we make it relivant to the htmleditor.
28497 Roo.htmleditor.Block.cache = {};
28498
28499 Roo.htmleditor.Block.prototype = {
28500     
28501     node : false,
28502     
28503      // used by context menu
28504     friendly_name : 'Based Block',
28505     
28506     // text for button to delete this element
28507     deleteTitle : false,
28508     
28509     context : false,
28510     /**
28511      * Update a node with values from this object
28512      * @param {DomElement} node
28513      */
28514     updateElement : function(node)
28515     {
28516         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
28517     },
28518      /**
28519      * convert to plain HTML for calling insertAtCursor..
28520      */
28521     toHTML : function()
28522     {
28523         return Roo.DomHelper.markup(this.toObject());
28524     },
28525     /**
28526      * used by readEleemnt to extract data from a node
28527      * may need improving as it's pretty basic
28528      
28529      * @param {DomElement} node
28530      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
28531      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
28532      * @param {String} style the style property - eg. text-align
28533      */
28534     getVal : function(node, tag, attr, style)
28535     {
28536         var n = node;
28537         if (tag !== true && n.tagName != tag.toUpperCase()) {
28538             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
28539             // but kiss for now.
28540             n = node.getElementsByTagName(tag).item(0);
28541         }
28542         if (!n) {
28543             return '';
28544         }
28545         if (attr === false) {
28546             return n;
28547         }
28548         if (attr == 'html') {
28549             return n.innerHTML;
28550         }
28551         if (attr == 'style') {
28552             return n.style[style]; 
28553         }
28554         
28555         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
28556             
28557     },
28558     /**
28559      * create a DomHelper friendly object - for use with 
28560      * Roo.DomHelper.markup / overwrite / etc..
28561      * (override this)
28562      */
28563     toObject : function()
28564     {
28565         return {};
28566     },
28567       /**
28568      * Read a node that has a 'data-block' property - and extract the values from it.
28569      * @param {DomElement} node - the node
28570      */
28571     readElement : function(node)
28572     {
28573         
28574     } 
28575     
28576     
28577 };
28578
28579  
28580
28581 /**
28582  * @class Roo.htmleditor.BlockFigure
28583  * Block that has an image and a figcaption
28584  * @cfg {String} image_src the url for the image
28585  * @cfg {String} align (left|right) alignment for the block default left
28586  * @cfg {String} caption the text to appear below  (and in the alt tag)
28587  * @cfg {String} caption_display (block|none) display or not the caption
28588  * @cfg {String|number} image_width the width of the image number or %?
28589  * @cfg {String|number} image_height the height of the image number or %?
28590  * 
28591  * @constructor
28592  * Create a new Filter.
28593  * @param {Object} config Configuration options
28594  */
28595
28596 Roo.htmleditor.BlockFigure = function(cfg)
28597 {
28598     if (cfg.node) {
28599         this.readElement(cfg.node);
28600         this.updateElement(cfg.node);
28601     }
28602     Roo.apply(this, cfg);
28603 }
28604 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
28605  
28606     
28607     // setable values.
28608     image_src: '',
28609     align: 'center',
28610     caption : '',
28611     caption_display : 'block',
28612     width : '100%',
28613     cls : '',
28614     href: '',
28615     video_url : '',
28616     
28617     // margin: '2%', not used
28618     
28619     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
28620
28621     
28622     // used by context menu
28623     friendly_name : 'Image with caption',
28624     deleteTitle : "Delete Image and Caption",
28625     
28626     contextMenu : function(toolbar)
28627     {
28628         
28629         var block = function() {
28630             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
28631         };
28632         
28633         
28634         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
28635         
28636         var syncValue = toolbar.editorcore.syncValue;
28637         
28638         var fields = {};
28639         
28640         return [
28641              {
28642                 xtype : 'TextItem',
28643                 text : "Source: ",
28644                 xns : rooui.Toolbar  //Boostrap?
28645             },
28646             {
28647                 xtype : 'Button',
28648                 text: 'Change Image URL',
28649                  
28650                 listeners : {
28651                     click: function (btn, state)
28652                     {
28653                         var b = block();
28654                         
28655                         Roo.MessageBox.show({
28656                             title : "Image Source URL",
28657                             msg : "Enter the url for the image",
28658                             buttons: Roo.MessageBox.OKCANCEL,
28659                             fn: function(btn, val){
28660                                 if (btn != 'ok') {
28661                                     return;
28662                                 }
28663                                 b.image_src = val;
28664                                 b.updateElement();
28665                                 syncValue();
28666                                 toolbar.editorcore.onEditorEvent();
28667                             },
28668                             minWidth:250,
28669                             prompt:true,
28670                             //multiline: multiline,
28671                             modal : true,
28672                             value : b.image_src
28673                         });
28674                     }
28675                 },
28676                 xns : rooui.Toolbar
28677             },
28678          
28679             {
28680                 xtype : 'Button',
28681                 text: 'Change Link URL',
28682                  
28683                 listeners : {
28684                     click: function (btn, state)
28685                     {
28686                         var b = block();
28687                         
28688                         Roo.MessageBox.show({
28689                             title : "Link URL",
28690                             msg : "Enter the url for the link - leave blank to have no link",
28691                             buttons: Roo.MessageBox.OKCANCEL,
28692                             fn: function(btn, val){
28693                                 if (btn != 'ok') {
28694                                     return;
28695                                 }
28696                                 b.href = val;
28697                                 b.updateElement();
28698                                 syncValue();
28699                                 toolbar.editorcore.onEditorEvent();
28700                             },
28701                             minWidth:250,
28702                             prompt:true,
28703                             //multiline: multiline,
28704                             modal : true,
28705                             value : b.href
28706                         });
28707                     }
28708                 },
28709                 xns : rooui.Toolbar
28710             },
28711             {
28712                 xtype : 'Button',
28713                 text: 'Show Video URL',
28714                  
28715                 listeners : {
28716                     click: function (btn, state)
28717                     {
28718                         Roo.MessageBox.alert("Video URL",
28719                             block().video_url == '' ? 'This image is not linked ot a video' :
28720                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
28721                     }
28722                 },
28723                 xns : rooui.Toolbar
28724             },
28725             
28726             
28727             {
28728                 xtype : 'TextItem',
28729                 text : "Width: ",
28730                 xns : rooui.Toolbar  //Boostrap?
28731             },
28732             {
28733                 xtype : 'ComboBox',
28734                 allowBlank : false,
28735                 displayField : 'val',
28736                 editable : true,
28737                 listWidth : 100,
28738                 triggerAction : 'all',
28739                 typeAhead : true,
28740                 valueField : 'val',
28741                 width : 70,
28742                 name : 'width',
28743                 listeners : {
28744                     select : function (combo, r, index)
28745                     {
28746                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28747                         var b = block();
28748                         b.width = r.get('val');
28749                         b.updateElement();
28750                         syncValue();
28751                         toolbar.editorcore.onEditorEvent();
28752                     }
28753                 },
28754                 xns : rooui.form,
28755                 store : {
28756                     xtype : 'SimpleStore',
28757                     data : [
28758                         ['100%'],
28759                         ['80%'],
28760                         ['50%'],
28761                         ['20%'],
28762                         ['10%']
28763                     ],
28764                     fields : [ 'val'],
28765                     xns : Roo.data
28766                 }
28767             },
28768             {
28769                 xtype : 'TextItem',
28770                 text : "Align: ",
28771                 xns : rooui.Toolbar  //Boostrap?
28772             },
28773             {
28774                 xtype : 'ComboBox',
28775                 allowBlank : false,
28776                 displayField : 'val',
28777                 editable : true,
28778                 listWidth : 100,
28779                 triggerAction : 'all',
28780                 typeAhead : true,
28781                 valueField : 'val',
28782                 width : 70,
28783                 name : 'align',
28784                 listeners : {
28785                     select : function (combo, r, index)
28786                     {
28787                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28788                         var b = block();
28789                         b.align = r.get('val');
28790                         b.updateElement();
28791                         syncValue();
28792                         toolbar.editorcore.onEditorEvent();
28793                     }
28794                 },
28795                 xns : rooui.form,
28796                 store : {
28797                     xtype : 'SimpleStore',
28798                     data : [
28799                         ['left'],
28800                         ['right'],
28801                         ['center']
28802                     ],
28803                     fields : [ 'val'],
28804                     xns : Roo.data
28805                 }
28806             },
28807             
28808             
28809             {
28810                 xtype : 'Button',
28811                 text: 'Hide Caption',
28812                 name : 'caption_display',
28813                 pressed : false,
28814                 enableToggle : true,
28815                 setValue : function(v) {
28816                     // this trigger toggle.
28817                      
28818                     this.setText(v ? "Hide Caption" : "Show Caption");
28819                     this.setPressed(v != 'block');
28820                 },
28821                 listeners : {
28822                     toggle: function (btn, state)
28823                     {
28824                         var b  = block();
28825                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
28826                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
28827                         b.updateElement();
28828                         syncValue();
28829                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28830                         toolbar.editorcore.onEditorEvent();
28831                     }
28832                 },
28833                 xns : rooui.Toolbar
28834             }
28835         ];
28836         
28837     },
28838     /**
28839      * create a DomHelper friendly object - for use with
28840      * Roo.DomHelper.markup / overwrite / etc..
28841      */
28842     toObject : function()
28843     {
28844         var d = document.createElement('div');
28845         d.innerHTML = this.caption;
28846         
28847         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
28848         
28849         var iw = this.align == 'center' ? this.width : '100%';
28850         var img =   {
28851             tag : 'img',
28852             contenteditable : 'false',
28853             src : this.image_src,
28854             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
28855             style: {
28856                 width : iw,
28857                 maxWidth : iw + ' !important', // this is not getting rendered?
28858                 margin : m  
28859                 
28860             }
28861         };
28862         /*
28863         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
28864                     '<a href="{2}">' + 
28865                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
28866                     '</a>' + 
28867                 '</div>',
28868         */
28869                 
28870         if (this.href.length > 0) {
28871             img = {
28872                 tag : 'a',
28873                 href: this.href,
28874                 contenteditable : 'true',
28875                 cn : [
28876                     img
28877                 ]
28878             };
28879         }
28880         
28881         
28882         if (this.video_url.length > 0) {
28883             img = {
28884                 tag : 'div',
28885                 cls : this.cls,
28886                 frameborder : 0,
28887                 allowfullscreen : true,
28888                 width : 420,  // these are for video tricks - that we replace the outer
28889                 height : 315,
28890                 src : this.video_url,
28891                 cn : [
28892                     img
28893                 ]
28894             };
28895         }
28896         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
28897         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
28898         
28899   
28900         var ret =   {
28901             tag: 'figure',
28902             'data-block' : 'Figure',
28903             'data-width' : this.width, 
28904             contenteditable : 'false',
28905             
28906             style : {
28907                 display: 'block',
28908                 float :  this.align ,
28909                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
28910                 width : this.align == 'center' ? '100%' : this.width,
28911                 margin:  '0px',
28912                 padding: this.align == 'center' ? '0' : '0 10px' ,
28913                 textAlign : this.align   // seems to work for email..
28914                 
28915             },
28916            
28917             
28918             align : this.align,
28919             cn : [
28920                 img,
28921               
28922                 {
28923                     tag: 'figcaption',
28924                     'data-display' : this.caption_display,
28925                     style : {
28926                         textAlign : 'left',
28927                         fontSize : '16px',
28928                         lineHeight : '24px',
28929                         display : this.caption_display,
28930                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
28931                         margin: m,
28932                         width: this.align == 'center' ?  this.width : '100%' 
28933                     
28934                          
28935                     },
28936                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
28937                     cn : [
28938                         {
28939                             tag: 'div',
28940                             style  : {
28941                                 marginTop : '16px',
28942                                 textAlign : 'left'
28943                             },
28944                             align: 'left',
28945                             cn : [
28946                                 {
28947                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
28948                                     tag : 'i',
28949                                     contenteditable : true,
28950                                     html : captionhtml
28951                                 }
28952                                 
28953                             ]
28954                         }
28955                         
28956                     ]
28957                     
28958                 }
28959             ]
28960         };
28961         return ret;
28962          
28963     },
28964     
28965     readElement : function(node)
28966     {
28967         // this should not really come from the link...
28968         this.video_url = this.getVal(node, 'div', 'src');
28969         this.cls = this.getVal(node, 'div', 'class');
28970         this.href = this.getVal(node, 'a', 'href');
28971         
28972         
28973         this.image_src = this.getVal(node, 'img', 'src');
28974          
28975         this.align = this.getVal(node, 'figure', 'align');
28976         var figcaption = this.getVal(node, 'figcaption', false);
28977         if (figcaption !== '') {
28978             this.caption = this.getVal(figcaption, 'i', 'html');
28979         }
28980         
28981
28982         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
28983         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
28984         this.width = this.getVal(node, true, 'data-width');
28985         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
28986         
28987     },
28988     removeNode : function()
28989     {
28990         return this.node;
28991     }
28992     
28993   
28994    
28995      
28996     
28997     
28998     
28999     
29000 })
29001
29002  
29003
29004 /**
29005  * @class Roo.htmleditor.BlockTable
29006  * Block that manages a table
29007  * 
29008  * @constructor
29009  * Create a new Filter.
29010  * @param {Object} config Configuration options
29011  */
29012
29013 Roo.htmleditor.BlockTable = function(cfg)
29014 {
29015     if (cfg.node) {
29016         this.readElement(cfg.node);
29017         this.updateElement(cfg.node);
29018     }
29019     Roo.apply(this, cfg);
29020     if (!cfg.node) {
29021         this.rows = [];
29022         for(var r = 0; r < this.no_row; r++) {
29023             this.rows[r] = [];
29024             for(var c = 0; c < this.no_col; c++) {
29025                 this.rows[r][c] = this.emptyCell();
29026             }
29027         }
29028     }
29029     
29030     
29031 }
29032 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29033  
29034     rows : false,
29035     no_col : 1,
29036     no_row : 1,
29037     
29038     
29039     width: '100%',
29040     
29041     // used by context menu
29042     friendly_name : 'Table',
29043     deleteTitle : 'Delete Table',
29044     // context menu is drawn once..
29045     
29046     contextMenu : function(toolbar)
29047     {
29048         
29049         var block = function() {
29050             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29051         };
29052         
29053         
29054         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29055         
29056         var syncValue = toolbar.editorcore.syncValue;
29057         
29058         var fields = {};
29059         
29060         return [
29061             {
29062                 xtype : 'TextItem',
29063                 text : "Width: ",
29064                 xns : rooui.Toolbar  //Boostrap?
29065             },
29066             {
29067                 xtype : 'ComboBox',
29068                 allowBlank : false,
29069                 displayField : 'val',
29070                 editable : true,
29071                 listWidth : 100,
29072                 triggerAction : 'all',
29073                 typeAhead : true,
29074                 valueField : 'val',
29075                 width : 100,
29076                 name : 'width',
29077                 listeners : {
29078                     select : function (combo, r, index)
29079                     {
29080                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29081                         var b = block();
29082                         b.width = r.get('val');
29083                         b.updateElement();
29084                         syncValue();
29085                         toolbar.editorcore.onEditorEvent();
29086                     }
29087                 },
29088                 xns : rooui.form,
29089                 store : {
29090                     xtype : 'SimpleStore',
29091                     data : [
29092                         ['100%'],
29093                         ['auto']
29094                     ],
29095                     fields : [ 'val'],
29096                     xns : Roo.data
29097                 }
29098             },
29099             // -------- Cols
29100             
29101             {
29102                 xtype : 'TextItem',
29103                 text : "Columns: ",
29104                 xns : rooui.Toolbar  //Boostrap?
29105             },
29106          
29107             {
29108                 xtype : 'Button',
29109                 text: '-',
29110                 listeners : {
29111                     click : function (_self, e)
29112                     {
29113                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29114                         block().removeColumn();
29115                         syncValue();
29116                         toolbar.editorcore.onEditorEvent();
29117                     }
29118                 },
29119                 xns : rooui.Toolbar
29120             },
29121             {
29122                 xtype : 'Button',
29123                 text: '+',
29124                 listeners : {
29125                     click : function (_self, e)
29126                     {
29127                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29128                         block().addColumn();
29129                         syncValue();
29130                         toolbar.editorcore.onEditorEvent();
29131                     }
29132                 },
29133                 xns : rooui.Toolbar
29134             },
29135             // -------- ROWS
29136             {
29137                 xtype : 'TextItem',
29138                 text : "Rows: ",
29139                 xns : rooui.Toolbar  //Boostrap?
29140             },
29141          
29142             {
29143                 xtype : 'Button',
29144                 text: '-',
29145                 listeners : {
29146                     click : function (_self, e)
29147                     {
29148                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29149                         block().removeRow();
29150                         syncValue();
29151                         toolbar.editorcore.onEditorEvent();
29152                     }
29153                 },
29154                 xns : rooui.Toolbar
29155             },
29156             {
29157                 xtype : 'Button',
29158                 text: '+',
29159                 listeners : {
29160                     click : function (_self, e)
29161                     {
29162                         block().addRow();
29163                         syncValue();
29164                         toolbar.editorcore.onEditorEvent();
29165                     }
29166                 },
29167                 xns : rooui.Toolbar
29168             },
29169             // -------- ROWS
29170             {
29171                 xtype : 'Button',
29172                 text: 'Reset Column Widths',
29173                 listeners : {
29174                     
29175                     click : function (_self, e)
29176                     {
29177                         block().resetWidths();
29178                         syncValue();
29179                         toolbar.editorcore.onEditorEvent();
29180                     }
29181                 },
29182                 xns : rooui.Toolbar
29183             } 
29184             
29185             
29186             
29187         ];
29188         
29189     },
29190     
29191     
29192   /**
29193      * create a DomHelper friendly object - for use with
29194      * Roo.DomHelper.markup / overwrite / etc..
29195      * ?? should it be called with option to hide all editing features?
29196      */
29197     toObject : function()
29198     {
29199         
29200         var ret = {
29201             tag : 'table',
29202             contenteditable : 'false', // this stops cell selection from picking the table.
29203             'data-block' : 'Table',
29204             style : {
29205                 width:  this.width,
29206                 border : 'solid 1px #000', // ??? hard coded?
29207                 'border-collapse' : 'collapse' 
29208             },
29209             cn : [
29210                 { tag : 'tbody' , cn : [] }
29211             ]
29212         };
29213         
29214         // do we have a head = not really 
29215         var ncols = 0;
29216         Roo.each(this.rows, function( row ) {
29217             var tr = {
29218                 tag: 'tr',
29219                 style : {
29220                     margin: '6px',
29221                     border : 'solid 1px #000',
29222                     textAlign : 'left' 
29223                 },
29224                 cn : [ ]
29225             };
29226             
29227             ret.cn[0].cn.push(tr);
29228             // does the row have any properties? ?? height?
29229             var nc = 0;
29230             Roo.each(row, function( cell ) {
29231                 
29232                 var td = {
29233                     tag : 'td',
29234                     contenteditable :  'true',
29235                     'data-block' : 'Td',
29236                     html : cell.html,
29237                     style : cell.style
29238                 };
29239                 if (cell.colspan > 1) {
29240                     td.colspan = cell.colspan ;
29241                     nc += cell.colspan;
29242                 } else {
29243                     nc++;
29244                 }
29245                 if (cell.rowspan > 1) {
29246                     td.rowspan = cell.rowspan ;
29247                 }
29248                 
29249                 
29250                 // widths ?
29251                 tr.cn.push(td);
29252                     
29253                 
29254             }, this);
29255             ncols = Math.max(nc, ncols);
29256             
29257             
29258         }, this);
29259         // add the header row..
29260         
29261         ncols++;
29262          
29263         
29264         return ret;
29265          
29266     },
29267     
29268     readElement : function(node)
29269     {
29270         node  = node ? node : this.node ;
29271         this.width = this.getVal(node, true, 'style', 'width') || '100%';
29272         
29273         this.rows = [];
29274         this.no_row = 0;
29275         var trs = Array.from(node.rows);
29276         trs.forEach(function(tr) {
29277             var row =  [];
29278             this.rows.push(row);
29279             
29280             this.no_row++;
29281             var no_column = 0;
29282             Array.from(tr.cells).forEach(function(td) {
29283                 
29284                 var add = {
29285                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29286                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29287                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29288                     html : td.innerHTML
29289                 };
29290                 no_column += add.colspan;
29291                      
29292                 
29293                 row.push(add);
29294                 
29295                 
29296             },this);
29297             this.no_col = Math.max(this.no_col, no_column);
29298             
29299             
29300         },this);
29301         
29302         
29303     },
29304     normalizeRows: function()
29305     {
29306         var ret= [];
29307         var rid = -1;
29308         this.rows.forEach(function(row) {
29309             rid++;
29310             ret[rid] = [];
29311             row = this.normalizeRow(row);
29312             var cid = 0;
29313             row.forEach(function(c) {
29314                 while (typeof(ret[rid][cid]) != 'undefined') {
29315                     cid++;
29316                 }
29317                 if (typeof(ret[rid]) == 'undefined') {
29318                     ret[rid] = [];
29319                 }
29320                 ret[rid][cid] = c;
29321                 c.row = rid;
29322                 c.col = cid;
29323                 if (c.rowspan < 2) {
29324                     return;
29325                 }
29326                 
29327                 for(var i = 1 ;i < c.rowspan; i++) {
29328                     if (typeof(ret[rid+i]) == 'undefined') {
29329                         ret[rid+i] = [];
29330                     }
29331                     ret[rid+i][cid] = c;
29332                 }
29333             });
29334         }, this);
29335         return ret;
29336     
29337     },
29338     
29339     normalizeRow: function(row)
29340     {
29341         var ret= [];
29342         row.forEach(function(c) {
29343             if (c.colspan < 2) {
29344                 ret.push(c);
29345                 return;
29346             }
29347             for(var i =0 ;i < c.colspan; i++) {
29348                 ret.push(c);
29349             }
29350         });
29351         return ret;
29352     
29353     },
29354     
29355     deleteColumn : function(sel)
29356     {
29357         if (!sel || sel.type != 'col') {
29358             return;
29359         }
29360         if (this.no_col < 2) {
29361             return;
29362         }
29363         
29364         this.rows.forEach(function(row) {
29365             var cols = this.normalizeRow(row);
29366             var col = cols[sel.col];
29367             if (col.colspan > 1) {
29368                 col.colspan --;
29369             } else {
29370                 row.remove(col);
29371             }
29372             
29373         }, this);
29374         this.no_col--;
29375         
29376     },
29377     removeColumn : function()
29378     {
29379         this.deleteColumn({
29380             type: 'col',
29381             col : this.no_col-1
29382         });
29383         this.updateElement();
29384     },
29385     
29386      
29387     addColumn : function()
29388     {
29389         
29390         this.rows.forEach(function(row) {
29391             row.push(this.emptyCell());
29392            
29393         }, this);
29394         this.updateElement();
29395     },
29396     
29397     deleteRow : function(sel)
29398     {
29399         if (!sel || sel.type != 'row') {
29400             return;
29401         }
29402         
29403         if (this.no_row < 2) {
29404             return;
29405         }
29406         
29407         var rows = this.normalizeRows();
29408         
29409         
29410         rows[sel.row].forEach(function(col) {
29411             if (col.rowspan > 1) {
29412                 col.rowspan--;
29413             } else {
29414                 col.remove = 1; // flage it as removed.
29415             }
29416             
29417         }, this);
29418         var newrows = [];
29419         this.rows.forEach(function(row) {
29420             newrow = [];
29421             row.forEach(function(c) {
29422                 if (typeof(c.remove) == 'undefined') {
29423                     newrow.push(c);
29424                 }
29425                 
29426             });
29427             if (newrow.length > 0) {
29428                 newrows.push(row);
29429             }
29430         });
29431         this.rows =  newrows;
29432         
29433         
29434         
29435         this.no_row--;
29436         this.updateElement();
29437         
29438     },
29439     removeRow : function()
29440     {
29441         this.deleteRow({
29442             type: 'row',
29443             row : this.no_row-1
29444         });
29445         
29446     },
29447     
29448      
29449     addRow : function()
29450     {
29451         
29452         var row = [];
29453         for (var i = 0; i < this.no_col; i++ ) {
29454             
29455             row.push(this.emptyCell());
29456            
29457         }
29458         this.rows.push(row);
29459         this.updateElement();
29460         
29461     },
29462      
29463     // the default cell object... at present...
29464     emptyCell : function() {
29465         return (new Roo.htmleditor.BlockTd({})).toObject();
29466         
29467      
29468     },
29469     
29470     removeNode : function()
29471     {
29472         return this.node;
29473     },
29474     
29475     
29476     
29477     resetWidths : function()
29478     {
29479         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
29480             var nn = Roo.htmleditor.Block.factory(n);
29481             nn.width = '';
29482             nn.updateElement(n);
29483         });
29484     }
29485     
29486     
29487     
29488     
29489 })
29490
29491 /**
29492  *
29493  * editing a TD?
29494  *
29495  * since selections really work on the table cell, then editing really should work from there
29496  *
29497  * The original plan was to support merging etc... - but that may not be needed yet..
29498  *
29499  * So this simple version will support:
29500  *   add/remove cols
29501  *   adjust the width +/-
29502  *   reset the width...
29503  *   
29504  *
29505  */
29506
29507
29508  
29509
29510 /**
29511  * @class Roo.htmleditor.BlockTable
29512  * Block that manages a table
29513  * 
29514  * @constructor
29515  * Create a new Filter.
29516  * @param {Object} config Configuration options
29517  */
29518
29519 Roo.htmleditor.BlockTd = function(cfg)
29520 {
29521     if (cfg.node) {
29522         this.readElement(cfg.node);
29523         this.updateElement(cfg.node);
29524     }
29525     Roo.apply(this, cfg);
29526      
29527     
29528     
29529 }
29530 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
29531  
29532     node : false,
29533     
29534     width: '',
29535     textAlign : 'left',
29536     valign : 'top',
29537     
29538     colspan : 1,
29539     rowspan : 1,
29540     
29541     
29542     // used by context menu
29543     friendly_name : 'Table Cell',
29544     deleteTitle : false, // use our customer delete
29545     
29546     // context menu is drawn once..
29547     
29548     contextMenu : function(toolbar)
29549     {
29550         
29551         var cell = function() {
29552             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29553         };
29554         
29555         var table = function() {
29556             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
29557         };
29558         
29559         var lr = false;
29560         var saveSel = function()
29561         {
29562             lr = toolbar.editorcore.getSelection().getRangeAt(0);
29563         }
29564         var restoreSel = function()
29565         {
29566             if (lr) {
29567                 (function() {
29568                     toolbar.editorcore.focus();
29569                     var cr = toolbar.editorcore.getSelection();
29570                     cr.removeAllRanges();
29571                     cr.addRange(lr);
29572                     toolbar.editorcore.onEditorEvent();
29573                 }).defer(10, this);
29574                 
29575                 
29576             }
29577         }
29578         
29579         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29580         
29581         var syncValue = toolbar.editorcore.syncValue;
29582         
29583         var fields = {};
29584         
29585         return [
29586             {
29587                 xtype : 'Button',
29588                 text : 'Edit Table',
29589                 listeners : {
29590                     click : function() {
29591                         var t = toolbar.tb.selectedNode.closest('table');
29592                         toolbar.editorcore.selectNode(t);
29593                         toolbar.editorcore.onEditorEvent();                        
29594                     }
29595                 }
29596                 
29597             },
29598               
29599            
29600              
29601             {
29602                 xtype : 'TextItem',
29603                 text : "Column Width: ",
29604                  xns : rooui.Toolbar 
29605                
29606             },
29607             {
29608                 xtype : 'Button',
29609                 text: '-',
29610                 listeners : {
29611                     click : function (_self, e)
29612                     {
29613                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29614                         cell().shrinkColumn();
29615                         syncValue();
29616                          toolbar.editorcore.onEditorEvent();
29617                     }
29618                 },
29619                 xns : rooui.Toolbar
29620             },
29621             {
29622                 xtype : 'Button',
29623                 text: '+',
29624                 listeners : {
29625                     click : function (_self, e)
29626                     {
29627                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29628                         cell().growColumn();
29629                         syncValue();
29630                         toolbar.editorcore.onEditorEvent();
29631                     }
29632                 },
29633                 xns : rooui.Toolbar
29634             },
29635             
29636             {
29637                 xtype : 'TextItem',
29638                 text : "Vertical Align: ",
29639                 xns : rooui.Toolbar  //Boostrap?
29640             },
29641             {
29642                 xtype : 'ComboBox',
29643                 allowBlank : false,
29644                 displayField : 'val',
29645                 editable : true,
29646                 listWidth : 100,
29647                 triggerAction : 'all',
29648                 typeAhead : true,
29649                 valueField : 'val',
29650                 width : 100,
29651                 name : 'valign',
29652                 listeners : {
29653                     select : function (combo, r, index)
29654                     {
29655                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29656                         var b = cell();
29657                         b.valign = r.get('val');
29658                         b.updateElement();
29659                         syncValue();
29660                         toolbar.editorcore.onEditorEvent();
29661                     }
29662                 },
29663                 xns : rooui.form,
29664                 store : {
29665                     xtype : 'SimpleStore',
29666                     data : [
29667                         ['top'],
29668                         ['middle'],
29669                         ['bottom'] // there are afew more... 
29670                     ],
29671                     fields : [ 'val'],
29672                     xns : Roo.data
29673                 }
29674             },
29675             
29676             {
29677                 xtype : 'TextItem',
29678                 text : "Merge Cells: ",
29679                  xns : rooui.Toolbar 
29680                
29681             },
29682             
29683             
29684             {
29685                 xtype : 'Button',
29686                 text: 'Right',
29687                 listeners : {
29688                     click : function (_self, e)
29689                     {
29690                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29691                         cell().mergeRight();
29692                         //block().growColumn();
29693                         syncValue();
29694                         toolbar.editorcore.onEditorEvent();
29695                     }
29696                 },
29697                 xns : rooui.Toolbar
29698             },
29699              
29700             {
29701                 xtype : 'Button',
29702                 text: 'Below',
29703                 listeners : {
29704                     click : function (_self, e)
29705                     {
29706                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29707                         cell().mergeBelow();
29708                         //block().growColumn();
29709                         syncValue();
29710                         toolbar.editorcore.onEditorEvent();
29711                     }
29712                 },
29713                 xns : rooui.Toolbar
29714             },
29715             {
29716                 xtype : 'TextItem',
29717                 text : "| ",
29718                  xns : rooui.Toolbar 
29719                
29720             },
29721             
29722             {
29723                 xtype : 'Button',
29724                 text: 'Split',
29725                 listeners : {
29726                     click : function (_self, e)
29727                     {
29728                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29729                         cell().split();
29730                         syncValue();
29731                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29732                         toolbar.editorcore.onEditorEvent();
29733                                              
29734                     }
29735                 },
29736                 xns : rooui.Toolbar
29737             },
29738             {
29739                 xtype : 'Fill',
29740                 xns : rooui.Toolbar 
29741                
29742             },
29743         
29744           
29745             {
29746                 xtype : 'Button',
29747                 text: 'Delete',
29748                  
29749                 xns : rooui.Toolbar,
29750                 menu : {
29751                     xtype : 'Menu',
29752                     xns : rooui.menu,
29753                     items : [
29754                         {
29755                             xtype : 'Item',
29756                             html: 'Column',
29757                             listeners : {
29758                                 click : function (_self, e)
29759                                 {
29760                                     var t = table();
29761                                     
29762                                     cell().deleteColumn();
29763                                     syncValue();
29764                                     toolbar.editorcore.selectNode(t.node);
29765                                     toolbar.editorcore.onEditorEvent();   
29766                                 }
29767                             },
29768                             xns : rooui.menu
29769                         },
29770                         {
29771                             xtype : 'Item',
29772                             html: 'Row',
29773                             listeners : {
29774                                 click : function (_self, e)
29775                                 {
29776                                     var t = table();
29777                                     cell().deleteRow();
29778                                     syncValue();
29779                                     
29780                                     toolbar.editorcore.selectNode(t.node);
29781                                     toolbar.editorcore.onEditorEvent();   
29782                                                          
29783                                 }
29784                             },
29785                             xns : rooui.menu
29786                         },
29787                        {
29788                             xtype : 'Separator',
29789                             xns : rooui.menu
29790                         },
29791                         {
29792                             xtype : 'Item',
29793                             html: 'Table',
29794                             listeners : {
29795                                 click : function (_self, e)
29796                                 {
29797                                     var t = table();
29798                                     var nn = t.node.nextSibling || t.node.previousSibling;
29799                                     t.node.parentNode.removeChild(t.node);
29800                                     if (nn) { 
29801                                         toolbar.editorcore.selectNode(nn, true);
29802                                     }
29803                                     toolbar.editorcore.onEditorEvent();   
29804                                                          
29805                                 }
29806                             },
29807                             xns : rooui.menu
29808                         }
29809                     ]
29810                 }
29811             }
29812             
29813             // align... << fixme
29814             
29815         ];
29816         
29817     },
29818     
29819     
29820   /**
29821      * create a DomHelper friendly object - for use with
29822      * Roo.DomHelper.markup / overwrite / etc..
29823      * ?? should it be called with option to hide all editing features?
29824      */
29825  /**
29826      * create a DomHelper friendly object - for use with
29827      * Roo.DomHelper.markup / overwrite / etc..
29828      * ?? should it be called with option to hide all editing features?
29829      */
29830     toObject : function()
29831     {
29832         var ret = {
29833             tag : 'td',
29834             contenteditable : 'true', // this stops cell selection from picking the table.
29835             'data-block' : 'Td',
29836             valign : this.valign,
29837             style : {  
29838                 'text-align' :  this.textAlign,
29839                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
29840                 'border-collapse' : 'collapse',
29841                 padding : '6px', // 8 for desktop / 4 for mobile
29842                 'vertical-align': this.valign
29843             },
29844             html : this.html
29845         };
29846         if (this.width != '') {
29847             ret.width = this.width;
29848             ret.style.width = this.width;
29849         }
29850         
29851         
29852         if (this.colspan > 1) {
29853             ret.colspan = this.colspan ;
29854         } 
29855         if (this.rowspan > 1) {
29856             ret.rowspan = this.rowspan ;
29857         }
29858         
29859            
29860         
29861         return ret;
29862          
29863     },
29864     
29865     readElement : function(node)
29866     {
29867         node  = node ? node : this.node ;
29868         this.width = node.style.width;
29869         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
29870         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
29871         this.html = node.innerHTML;
29872         if (node.style.textAlign != '') {
29873             this.textAlign = node.style.textAlign;
29874         }
29875         
29876         
29877     },
29878      
29879     // the default cell object... at present...
29880     emptyCell : function() {
29881         return {
29882             colspan :  1,
29883             rowspan :  1,
29884             textAlign : 'left',
29885             html : "&nbsp;" // is this going to be editable now?
29886         };
29887      
29888     },
29889     
29890     removeNode : function()
29891     {
29892         return this.node.closest('table');
29893          
29894     },
29895     
29896     cellData : false,
29897     
29898     colWidths : false,
29899     
29900     toTableArray  : function()
29901     {
29902         var ret = [];
29903         var tab = this.node.closest('tr').closest('table');
29904         Array.from(tab.rows).forEach(function(r, ri){
29905             ret[ri] = [];
29906         });
29907         var rn = 0;
29908         this.colWidths = [];
29909         var all_auto = true;
29910         Array.from(tab.rows).forEach(function(r, ri){
29911             
29912             var cn = 0;
29913             Array.from(r.cells).forEach(function(ce, ci){
29914                 var c =  {
29915                     cell : ce,
29916                     row : rn,
29917                     col: cn,
29918                     colspan : ce.colSpan,
29919                     rowspan : ce.rowSpan
29920                 };
29921                 if (ce.isEqualNode(this.node)) {
29922                     this.cellData = c;
29923                 }
29924                 // if we have been filled up by a row?
29925                 if (typeof(ret[rn][cn]) != 'undefined') {
29926                     while(typeof(ret[rn][cn]) != 'undefined') {
29927                         cn++;
29928                     }
29929                     c.col = cn;
29930                 }
29931                 
29932                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
29933                     this.colWidths[cn] =   ce.style.width;
29934                     if (this.colWidths[cn] != '') {
29935                         all_auto = false;
29936                     }
29937                 }
29938                 
29939                 
29940                 if (c.colspan < 2 && c.rowspan < 2 ) {
29941                     ret[rn][cn] = c;
29942                     cn++;
29943                     return;
29944                 }
29945                 for(var j = 0; j < c.rowspan; j++) {
29946                     if (typeof(ret[rn+j]) == 'undefined') {
29947                         continue; // we have a problem..
29948                     }
29949                     ret[rn+j][cn] = c;
29950                     for(var i = 0; i < c.colspan; i++) {
29951                         ret[rn+j][cn+i] = c;
29952                     }
29953                 }
29954                 
29955                 cn += c.colspan;
29956             }, this);
29957             rn++;
29958         }, this);
29959         
29960         // initalize widths.?
29961         // either all widths or no widths..
29962         if (all_auto) {
29963             this.colWidths[0] = false; // no widths flag.
29964         }
29965         
29966         
29967         return ret;
29968         
29969     },
29970     
29971     
29972     
29973     
29974     mergeRight: function()
29975     {
29976          
29977         // get the contents of the next cell along..
29978         var tr = this.node.closest('tr');
29979         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
29980         if (i >= tr.childNodes.length - 1) {
29981             return; // no cells on right to merge with.
29982         }
29983         var table = this.toTableArray();
29984         
29985         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
29986             return; // nothing right?
29987         }
29988         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
29989         // right cell - must be same rowspan and on the same row.
29990         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
29991             return; // right hand side is not same rowspan.
29992         }
29993         
29994         
29995         
29996         this.node.innerHTML += ' ' + rc.cell.innerHTML;
29997         tr.removeChild(rc.cell);
29998         this.colspan += rc.colspan;
29999         this.node.setAttribute('colspan', this.colspan);
30000
30001         var table = this.toTableArray();
30002         this.normalizeWidths(table);
30003         this.updateWidths(table);
30004     },
30005     
30006     
30007     mergeBelow : function()
30008     {
30009         var table = this.toTableArray();
30010         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30011             return; // no row below
30012         }
30013         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30014             return; // nothing right?
30015         }
30016         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30017         
30018         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30019             return; // right hand side is not same rowspan.
30020         }
30021         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
30022         rc.cell.parentNode.removeChild(rc.cell);
30023         this.rowspan += rc.rowspan;
30024         this.node.setAttribute('rowspan', this.rowspan);
30025     },
30026     
30027     split: function()
30028     {
30029         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30030             return;
30031         }
30032         var table = this.toTableArray();
30033         var cd = this.cellData;
30034         this.rowspan = 1;
30035         this.colspan = 1;
30036         
30037         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30038              
30039             
30040             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30041                 if (r == cd.row && c == cd.col) {
30042                     this.node.removeAttribute('rowspan');
30043                     this.node.removeAttribute('colspan');
30044                 }
30045                  
30046                 var ntd = this.node.cloneNode(); // which col/row should be 0..
30047                 ntd.removeAttribute('id'); 
30048                 ntd.style.width  = this.colWidths[c];
30049                 ntd.innerHTML = '';
30050                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
30051             }
30052             
30053         }
30054         this.redrawAllCells(table);
30055         
30056     },
30057     
30058     
30059     
30060     redrawAllCells: function(table)
30061     {
30062         
30063          
30064         var tab = this.node.closest('tr').closest('table');
30065         var ctr = tab.rows[0].parentNode;
30066         Array.from(tab.rows).forEach(function(r, ri){
30067             
30068             Array.from(r.cells).forEach(function(ce, ci){
30069                 ce.parentNode.removeChild(ce);
30070             });
30071             r.parentNode.removeChild(r);
30072         });
30073         for(var r = 0 ; r < table.length; r++) {
30074             var re = tab.rows[r];
30075             
30076             var re = tab.ownerDocument.createElement('tr');
30077             ctr.appendChild(re);
30078             for(var c = 0 ; c < table[r].length; c++) {
30079                 if (table[r][c].cell === false) {
30080                     continue;
30081                 }
30082                 
30083                 re.appendChild(table[r][c].cell);
30084                  
30085                 table[r][c].cell = false;
30086             }
30087         }
30088         
30089     },
30090     updateWidths : function(table)
30091     {
30092         for(var r = 0 ; r < table.length; r++) {
30093            
30094             for(var c = 0 ; c < table[r].length; c++) {
30095                 if (table[r][c].cell === false) {
30096                     continue;
30097                 }
30098                 
30099                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30100                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30101                     el.width = Math.floor(this.colWidths[c])  +'%';
30102                     el.updateElement(el.node);
30103                 }
30104                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30105                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30106                     var width = 0;
30107                     for(var i = 0; i < table[r][c].colspan; i ++) {
30108                         width += Math.floor(this.colWidths[c + i]);
30109                     }
30110                     el.width = width  +'%';
30111                     el.updateElement(el.node);
30112                 }
30113                 table[r][c].cell = false; // done
30114             }
30115         }
30116     },
30117     normalizeWidths : function(table)
30118     {
30119         if (this.colWidths[0] === false) {
30120             var nw = 100.0 / this.colWidths.length;
30121             this.colWidths.forEach(function(w,i) {
30122                 this.colWidths[i] = nw;
30123             },this);
30124             return;
30125         }
30126     
30127         var t = 0, missing = [];
30128         
30129         this.colWidths.forEach(function(w,i) {
30130             //if you mix % and
30131             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30132             var add =  this.colWidths[i];
30133             if (add > 0) {
30134                 t+=add;
30135                 return;
30136             }
30137             missing.push(i);
30138             
30139             
30140         },this);
30141         var nc = this.colWidths.length;
30142         if (missing.length) {
30143             var mult = (nc - missing.length) / (1.0 * nc);
30144             var t = mult * t;
30145             var ew = (100 -t) / (1.0 * missing.length);
30146             this.colWidths.forEach(function(w,i) {
30147                 if (w > 0) {
30148                     this.colWidths[i] = w * mult;
30149                     return;
30150                 }
30151                 
30152                 this.colWidths[i] = ew;
30153             }, this);
30154             // have to make up numbers..
30155              
30156         }
30157         // now we should have all the widths..
30158         
30159     
30160     },
30161     
30162     shrinkColumn : function()
30163     {
30164         var table = this.toTableArray();
30165         this.normalizeWidths(table);
30166         var col = this.cellData.col;
30167         var nw = this.colWidths[col] * 0.8;
30168         if (nw < 5) {
30169             return;
30170         }
30171         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30172         this.colWidths.forEach(function(w,i) {
30173             if (i == col) {
30174                  this.colWidths[i] = nw;
30175                 return;
30176             }
30177             this.colWidths[i] += otherAdd
30178         }, this);
30179         this.updateWidths(table);
30180          
30181     },
30182     growColumn : function()
30183     {
30184         var table = this.toTableArray();
30185         this.normalizeWidths(table);
30186         var col = this.cellData.col;
30187         var nw = this.colWidths[col] * 1.2;
30188         if (nw > 90) {
30189             return;
30190         }
30191         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30192         this.colWidths.forEach(function(w,i) {
30193             if (i == col) {
30194                 this.colWidths[i] = nw;
30195                 return;
30196             }
30197             this.colWidths[i] -= otherSub
30198         }, this);
30199         this.updateWidths(table);
30200          
30201     },
30202     deleteRow : function()
30203     {
30204         // delete this rows 'tr'
30205         // if any of the cells in this row have a rowspan > 1 && row!= this row..
30206         // then reduce the rowspan.
30207         var table = this.toTableArray();
30208         // this.cellData.row;
30209         for (var i =0;i< table[this.cellData.row].length ; i++) {
30210             var c = table[this.cellData.row][i];
30211             if (c.row != this.cellData.row) {
30212                 
30213                 c.rowspan--;
30214                 c.cell.setAttribute('rowspan', c.rowspan);
30215                 continue;
30216             }
30217             if (c.rowspan > 1) {
30218                 c.rowspan--;
30219                 c.cell.setAttribute('rowspan', c.rowspan);
30220             }
30221         }
30222         table.splice(this.cellData.row,1);
30223         this.redrawAllCells(table);
30224         
30225     },
30226     deleteColumn : function()
30227     {
30228         var table = this.toTableArray();
30229         
30230         for (var i =0;i< table.length ; i++) {
30231             var c = table[i][this.cellData.col];
30232             if (c.col != this.cellData.col) {
30233                 table[i][this.cellData.col].colspan--;
30234             } else if (c.colspan > 1) {
30235                 c.colspan--;
30236                 c.cell.setAttribute('colspan', c.colspan);
30237             }
30238             table[i].splice(this.cellData.col,1);
30239         }
30240         
30241         this.redrawAllCells(table);
30242     }
30243     
30244     
30245     
30246     
30247 })
30248
30249 //<script type="text/javascript">
30250
30251 /*
30252  * Based  Ext JS Library 1.1.1
30253  * Copyright(c) 2006-2007, Ext JS, LLC.
30254  * LGPL
30255  *
30256  */
30257  
30258 /**
30259  * @class Roo.HtmlEditorCore
30260  * @extends Roo.Component
30261  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30262  *
30263  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30264  */
30265
30266 Roo.HtmlEditorCore = function(config){
30267     
30268     
30269     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30270     
30271     
30272     this.addEvents({
30273         /**
30274          * @event initialize
30275          * Fires when the editor is fully initialized (including the iframe)
30276          * @param {Roo.HtmlEditorCore} this
30277          */
30278         initialize: true,
30279         /**
30280          * @event activate
30281          * Fires when the editor is first receives the focus. Any insertion must wait
30282          * until after this event.
30283          * @param {Roo.HtmlEditorCore} this
30284          */
30285         activate: true,
30286          /**
30287          * @event beforesync
30288          * Fires before the textarea is updated with content from the editor iframe. Return false
30289          * to cancel the sync.
30290          * @param {Roo.HtmlEditorCore} this
30291          * @param {String} html
30292          */
30293         beforesync: true,
30294          /**
30295          * @event beforepush
30296          * Fires before the iframe editor is updated with content from the textarea. Return false
30297          * to cancel the push.
30298          * @param {Roo.HtmlEditorCore} this
30299          * @param {String} html
30300          */
30301         beforepush: true,
30302          /**
30303          * @event sync
30304          * Fires when the textarea is updated with content from the editor iframe.
30305          * @param {Roo.HtmlEditorCore} this
30306          * @param {String} html
30307          */
30308         sync: true,
30309          /**
30310          * @event push
30311          * Fires when the iframe editor is updated with content from the textarea.
30312          * @param {Roo.HtmlEditorCore} this
30313          * @param {String} html
30314          */
30315         push: true,
30316         
30317         /**
30318          * @event editorevent
30319          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30320          * @param {Roo.HtmlEditorCore} this
30321          */
30322         editorevent: true 
30323          
30324         
30325     });
30326     
30327     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30328     
30329     // defaults : white / black...
30330     this.applyBlacklists();
30331     
30332     
30333     
30334 };
30335
30336
30337 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
30338
30339
30340      /**
30341      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
30342      */
30343     
30344     owner : false,
30345     
30346      /**
30347      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
30348      *                        Roo.resizable.
30349      */
30350     resizable : false,
30351      /**
30352      * @cfg {Number} height (in pixels)
30353      */   
30354     height: 300,
30355    /**
30356      * @cfg {Number} width (in pixels)
30357      */   
30358     width: 500,
30359      /**
30360      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30361      *         if you are doing an email editor, this probably needs disabling, it's designed
30362      */
30363     autoClean: true,
30364     
30365     /**
30366      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30367      */
30368     enableBlocks : true,
30369     /**
30370      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30371      * 
30372      */
30373     stylesheets: false,
30374      /**
30375      * @cfg {String} language default en - language of text (usefull for rtl languages)
30376      * 
30377      */
30378     language: 'en',
30379     
30380     /**
30381      * @cfg {boolean} allowComments - default false - allow comments in HTML source
30382      *          - by default they are stripped - if you are editing email you may need this.
30383      */
30384     allowComments: false,
30385     // id of frame..
30386     frameId: false,
30387     
30388     // private properties
30389     validationEvent : false,
30390     deferHeight: true,
30391     initialized : false,
30392     activated : false,
30393     sourceEditMode : false,
30394     onFocus : Roo.emptyFn,
30395     iframePad:3,
30396     hideMode:'offsets',
30397     
30398     clearUp: true,
30399     
30400     // blacklist + whitelisted elements..
30401     black: false,
30402     white: false,
30403      
30404     bodyCls : '',
30405
30406     
30407     undoManager : false,
30408     /**
30409      * Protected method that will not generally be called directly. It
30410      * is called when the editor initializes the iframe with HTML contents. Override this method if you
30411      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30412      */
30413     getDocMarkup : function(){
30414         // body styles..
30415         var st = '';
30416         
30417         // inherit styels from page...?? 
30418         if (this.stylesheets === false) {
30419             
30420             Roo.get(document.head).select('style').each(function(node) {
30421                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30422             });
30423             
30424             Roo.get(document.head).select('link').each(function(node) { 
30425                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30426             });
30427             
30428         } else if (!this.stylesheets.length) {
30429                 // simple..
30430                 st = '<style type="text/css">' +
30431                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30432                    '</style>';
30433         } else {
30434             for (var i in this.stylesheets) {
30435                 if (typeof(this.stylesheets[i]) != 'string') {
30436                     continue;
30437                 }
30438                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30439             }
30440             
30441         }
30442         
30443         st +=  '<style type="text/css">' +
30444             'IMG { cursor: pointer } ' +
30445         '</style>';
30446         
30447         st += '<meta name="google" content="notranslate">';
30448         
30449         var cls = 'notranslate roo-htmleditor-body';
30450         
30451         if(this.bodyCls.length){
30452             cls += ' ' + this.bodyCls;
30453         }
30454         
30455         return '<html  class="notranslate" translate="no"><head>' + st  +
30456             //<style type="text/css">' +
30457             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30458             //'</style>' +
30459             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
30460     },
30461
30462     // private
30463     onRender : function(ct, position)
30464     {
30465         var _t = this;
30466         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
30467         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
30468         
30469         
30470         this.el.dom.style.border = '0 none';
30471         this.el.dom.setAttribute('tabIndex', -1);
30472         this.el.addClass('x-hidden hide');
30473         
30474         
30475         
30476         if(Roo.isIE){ // fix IE 1px bogus margin
30477             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
30478         }
30479        
30480         
30481         this.frameId = Roo.id();
30482         
30483          
30484         
30485         var iframe = this.owner.wrap.createChild({
30486             tag: 'iframe',
30487             cls: 'form-control', // bootstrap..
30488             id: this.frameId,
30489             name: this.frameId,
30490             frameBorder : 'no',
30491             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
30492         }, this.el
30493         );
30494         
30495         
30496         this.iframe = iframe.dom;
30497
30498         this.assignDocWin();
30499         
30500         this.doc.designMode = 'on';
30501        
30502         this.doc.open();
30503         this.doc.write(this.getDocMarkup());
30504         this.doc.close();
30505
30506         
30507         var task = { // must defer to wait for browser to be ready
30508             run : function(){
30509                 //console.log("run task?" + this.doc.readyState);
30510                 this.assignDocWin();
30511                 if(this.doc.body || this.doc.readyState == 'complete'){
30512                     try {
30513                         this.doc.designMode="on";
30514                         
30515                     } catch (e) {
30516                         return;
30517                     }
30518                     Roo.TaskMgr.stop(task);
30519                     this.initEditor.defer(10, this);
30520                 }
30521             },
30522             interval : 10,
30523             duration: 10000,
30524             scope: this
30525         };
30526         Roo.TaskMgr.start(task);
30527
30528     },
30529
30530     // private
30531     onResize : function(w, h)
30532     {
30533          Roo.log('resize: ' +w + ',' + h );
30534         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
30535         if(!this.iframe){
30536             return;
30537         }
30538         if(typeof w == 'number'){
30539             
30540             this.iframe.style.width = w + 'px';
30541         }
30542         if(typeof h == 'number'){
30543             
30544             this.iframe.style.height = h + 'px';
30545             if(this.doc){
30546                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
30547             }
30548         }
30549         
30550     },
30551
30552     /**
30553      * Toggles the editor between standard and source edit mode.
30554      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
30555      */
30556     toggleSourceEdit : function(sourceEditMode){
30557         
30558         this.sourceEditMode = sourceEditMode === true;
30559         
30560         if(this.sourceEditMode){
30561  
30562             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
30563             
30564         }else{
30565             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
30566             //this.iframe.className = '';
30567             this.deferFocus();
30568         }
30569         //this.setSize(this.owner.wrap.getSize());
30570         //this.fireEvent('editmodechange', this, this.sourceEditMode);
30571     },
30572
30573     
30574   
30575
30576     /**
30577      * Protected method that will not generally be called directly. If you need/want
30578      * custom HTML cleanup, this is the method you should override.
30579      * @param {String} html The HTML to be cleaned
30580      * return {String} The cleaned HTML
30581      */
30582     cleanHtml : function(html)
30583     {
30584         html = String(html);
30585         if(html.length > 5){
30586             if(Roo.isSafari){ // strip safari nonsense
30587                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
30588             }
30589         }
30590         if(html == '&nbsp;'){
30591             html = '';
30592         }
30593         return html;
30594     },
30595
30596     /**
30597      * HTML Editor -> Textarea
30598      * Protected method that will not generally be called directly. Syncs the contents
30599      * of the editor iframe with the textarea.
30600      */
30601     syncValue : function()
30602     {
30603         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
30604         if(this.initialized){
30605             
30606             if (this.undoManager) {
30607                 this.undoManager.addEvent();
30608             }
30609
30610             
30611             var bd = (this.doc.body || this.doc.documentElement);
30612            
30613             
30614             var sel = this.win.getSelection();
30615             
30616             var div = document.createElement('div');
30617             div.innerHTML = bd.innerHTML;
30618             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
30619             if (gtx.length > 0) {
30620                 var rm = gtx.item(0).parentNode;
30621                 rm.parentNode.removeChild(rm);
30622             }
30623             
30624            
30625             if (this.enableBlocks) {
30626                 new Roo.htmleditor.FilterBlock({ node : div });
30627             }
30628             
30629             var html = div.innerHTML;
30630             
30631             //?? tidy?
30632             if (this.autoClean) {
30633                 
30634                 new Roo.htmleditor.FilterAttributes({
30635                     node : div,
30636                     attrib_white : [
30637                             'href',
30638                             'src',
30639                             'name',
30640                             'align',
30641                             'colspan',
30642                             'rowspan',
30643                             'data-display',
30644                             'data-width',
30645                             'start' ,
30646                             'style',
30647                             // youtube embed.
30648                             'class',
30649                             'allowfullscreen',
30650                             'frameborder',
30651                             'width',
30652                             'height',
30653                             'alt'
30654                             ],
30655                     attrib_clean : ['href', 'src' ] 
30656                 });
30657                 
30658                 var tidy = new Roo.htmleditor.TidySerializer({
30659                     inner:  true
30660                 });
30661                 html  = tidy.serialize(div);
30662                 
30663             }
30664             
30665             
30666             if(Roo.isSafari){
30667                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
30668                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
30669                 if(m && m[1]){
30670                     html = '<div style="'+m[0]+'">' + html + '</div>';
30671                 }
30672             }
30673             html = this.cleanHtml(html);
30674             // fix up the special chars.. normaly like back quotes in word...
30675             // however we do not want to do this with chinese..
30676             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
30677                 
30678                 var cc = match.charCodeAt();
30679
30680                 // Get the character value, handling surrogate pairs
30681                 if (match.length == 2) {
30682                     // It's a surrogate pair, calculate the Unicode code point
30683                     var high = match.charCodeAt(0) - 0xD800;
30684                     var low  = match.charCodeAt(1) - 0xDC00;
30685                     cc = (high * 0x400) + low + 0x10000;
30686                 }  else if (
30687                     (cc >= 0x4E00 && cc < 0xA000 ) ||
30688                     (cc >= 0x3400 && cc < 0x4E00 ) ||
30689                     (cc >= 0xf900 && cc < 0xfb00 )
30690                 ) {
30691                         return match;
30692                 }  
30693          
30694                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
30695                 return "&#" + cc + ";";
30696                 
30697                 
30698             });
30699             
30700             
30701              
30702             if(this.owner.fireEvent('beforesync', this, html) !== false){
30703                 this.el.dom.value = html;
30704                 this.owner.fireEvent('sync', this, html);
30705             }
30706         }
30707     },
30708
30709     /**
30710      * TEXTAREA -> EDITABLE
30711      * Protected method that will not generally be called directly. Pushes the value of the textarea
30712      * into the iframe editor.
30713      */
30714     pushValue : function()
30715     {
30716         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
30717         if(this.initialized){
30718             var v = this.el.dom.value.trim();
30719             
30720             
30721             if(this.owner.fireEvent('beforepush', this, v) !== false){
30722                 var d = (this.doc.body || this.doc.documentElement);
30723                 d.innerHTML = v;
30724                  
30725                 this.el.dom.value = d.innerHTML;
30726                 this.owner.fireEvent('push', this, v);
30727             }
30728             if (this.autoClean) {
30729                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
30730                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
30731             }
30732             if (this.enableBlocks) {
30733                 Roo.htmleditor.Block.initAll(this.doc.body);
30734             }
30735             
30736             this.updateLanguage();
30737             
30738             var lc = this.doc.body.lastChild;
30739             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
30740                 // add an extra line at the end.
30741                 this.doc.body.appendChild(this.doc.createElement('br'));
30742             }
30743             
30744             
30745         }
30746     },
30747
30748     // private
30749     deferFocus : function(){
30750         this.focus.defer(10, this);
30751     },
30752
30753     // doc'ed in Field
30754     focus : function(){
30755         if(this.win && !this.sourceEditMode){
30756             this.win.focus();
30757         }else{
30758             this.el.focus();
30759         }
30760     },
30761     
30762     assignDocWin: function()
30763     {
30764         var iframe = this.iframe;
30765         
30766          if(Roo.isIE){
30767             this.doc = iframe.contentWindow.document;
30768             this.win = iframe.contentWindow;
30769         } else {
30770 //            if (!Roo.get(this.frameId)) {
30771 //                return;
30772 //            }
30773 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
30774 //            this.win = Roo.get(this.frameId).dom.contentWindow;
30775             
30776             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
30777                 return;
30778             }
30779             
30780             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
30781             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
30782         }
30783     },
30784     
30785     // private
30786     initEditor : function(){
30787         //console.log("INIT EDITOR");
30788         this.assignDocWin();
30789         
30790         
30791         
30792         this.doc.designMode="on";
30793         this.doc.open();
30794         this.doc.write(this.getDocMarkup());
30795         this.doc.close();
30796         
30797         var dbody = (this.doc.body || this.doc.documentElement);
30798         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
30799         // this copies styles from the containing element into thsi one..
30800         // not sure why we need all of this..
30801         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
30802         
30803         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
30804         //ss['background-attachment'] = 'fixed'; // w3c
30805         dbody.bgProperties = 'fixed'; // ie
30806         dbody.setAttribute("translate", "no");
30807         
30808         //Roo.DomHelper.applyStyles(dbody, ss);
30809         Roo.EventManager.on(this.doc, {
30810              
30811             'mouseup': this.onEditorEvent,
30812             'dblclick': this.onEditorEvent,
30813             'click': this.onEditorEvent,
30814             'keyup': this.onEditorEvent,
30815             
30816             buffer:100,
30817             scope: this
30818         });
30819         Roo.EventManager.on(this.doc, {
30820             'paste': this.onPasteEvent,
30821             scope : this
30822         });
30823         if(Roo.isGecko){
30824             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
30825         }
30826         //??? needed???
30827         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
30828             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
30829         }
30830         this.initialized = true;
30831
30832         
30833         // initialize special key events - enter
30834         new Roo.htmleditor.KeyEnter({core : this});
30835         
30836          
30837         
30838         this.owner.fireEvent('initialize', this);
30839         this.pushValue();
30840     },
30841     // this is to prevent a href clicks resulting in a redirect?
30842    
30843     onPasteEvent : function(e,v)
30844     {
30845         // I think we better assume paste is going to be a dirty load of rubish from word..
30846         
30847         // even pasting into a 'email version' of this widget will have to clean up that mess.
30848         var cd = (e.browserEvent.clipboardData || window.clipboardData);
30849         
30850         // check what type of paste - if it's an image, then handle it differently.
30851         if (cd.files && cd.files.length > 0) {
30852             // pasting images?
30853             var urlAPI = (window.createObjectURL && window) || 
30854                 (window.URL && URL.revokeObjectURL && URL) || 
30855                 (window.webkitURL && webkitURL);
30856     
30857             var url = urlAPI.createObjectURL( cd.files[0]);
30858             this.insertAtCursor('<img src=" + url + ">');
30859             return false;
30860         }
30861         if (cd.types.indexOf('text/html') < 0 ) {
30862             return false;
30863         }
30864         var images = [];
30865         var html = cd.getData('text/html'); // clipboard event
30866         if (cd.types.indexOf('text/rtf') > -1) {
30867             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
30868             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
30869         }
30870         //Roo.log(images);
30871         //Roo.log(imgs);
30872         // fixme..
30873         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
30874                        .map(function(g) { return g.toDataURL(); })
30875                        .filter(function(g) { return g != 'about:blank'; });
30876         
30877         //Roo.log(html);
30878         html = this.cleanWordChars(html);
30879         
30880         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
30881         
30882         
30883         var sn = this.getParentElement();
30884         // check if d contains a table, and prevent nesting??
30885         //Roo.log(d.getElementsByTagName('table'));
30886         //Roo.log(sn);
30887         //Roo.log(sn.closest('table'));
30888         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
30889             e.preventDefault();
30890             this.insertAtCursor("You can not nest tables");
30891             //Roo.log("prevent?"); // fixme - 
30892             return false;
30893         }
30894         
30895         
30896         
30897         if (images.length > 0) {
30898             // replace all v:imagedata - with img.
30899             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
30900             Roo.each(ar, function(node) {
30901                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
30902                 node.parentNode.removeChild(node);
30903             });
30904             
30905             
30906             Roo.each(d.getElementsByTagName('img'), function(img, i) {
30907                 img.setAttribute('src', images[i]);
30908             });
30909         }
30910         if (this.autoClean) {
30911             new Roo.htmleditor.FilterWord({ node : d });
30912             
30913             new Roo.htmleditor.FilterStyleToTag({ node : d });
30914             new Roo.htmleditor.FilterAttributes({
30915                 node : d,
30916                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
30917                 attrib_clean : ['href', 'src' ] 
30918             });
30919             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
30920             // should be fonts..
30921             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
30922             new Roo.htmleditor.FilterParagraph({ node : d });
30923             new Roo.htmleditor.FilterSpan({ node : d });
30924             new Roo.htmleditor.FilterLongBr({ node : d });
30925             new Roo.htmleditor.FilterComment({ node : d });
30926             
30927             
30928         }
30929         if (this.enableBlocks) {
30930                 
30931             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
30932                 if (img.closest('figure')) { // assume!! that it's aready
30933                     return;
30934                 }
30935                 var fig  = new Roo.htmleditor.BlockFigure({
30936                     image_src  : img.src
30937                 });
30938                 fig.updateElement(img); // replace it..
30939                 
30940             });
30941         }
30942         
30943         
30944         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
30945         if (this.enableBlocks) {
30946             Roo.htmleditor.Block.initAll(this.doc.body);
30947         }
30948          
30949         
30950         e.preventDefault();
30951         return false;
30952         // default behaveiour should be our local cleanup paste? (optional?)
30953         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
30954         //this.owner.fireEvent('paste', e, v);
30955     },
30956     // private
30957     onDestroy : function(){
30958         
30959         
30960         
30961         if(this.rendered){
30962             
30963             //for (var i =0; i < this.toolbars.length;i++) {
30964             //    // fixme - ask toolbars for heights?
30965             //    this.toolbars[i].onDestroy();
30966            // }
30967             
30968             //this.wrap.dom.innerHTML = '';
30969             //this.wrap.remove();
30970         }
30971     },
30972
30973     // private
30974     onFirstFocus : function(){
30975         
30976         this.assignDocWin();
30977         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
30978         
30979         this.activated = true;
30980          
30981     
30982         if(Roo.isGecko){ // prevent silly gecko errors
30983             this.win.focus();
30984             var s = this.win.getSelection();
30985             if(!s.focusNode || s.focusNode.nodeType != 3){
30986                 var r = s.getRangeAt(0);
30987                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
30988                 r.collapse(true);
30989                 this.deferFocus();
30990             }
30991             try{
30992                 this.execCmd('useCSS', true);
30993                 this.execCmd('styleWithCSS', false);
30994             }catch(e){}
30995         }
30996         this.owner.fireEvent('activate', this);
30997     },
30998
30999     // private
31000     adjustFont: function(btn){
31001         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
31002         //if(Roo.isSafari){ // safari
31003         //    adjust *= 2;
31004        // }
31005         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31006         if(Roo.isSafari){ // safari
31007             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31008             v =  (v < 10) ? 10 : v;
31009             v =  (v > 48) ? 48 : v;
31010             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31011             
31012         }
31013         
31014         
31015         v = Math.max(1, v+adjust);
31016         
31017         this.execCmd('FontSize', v  );
31018     },
31019
31020     onEditorEvent : function(e)
31021     {
31022          
31023         
31024         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31025             return; // we do not handle this.. (undo manager does..)
31026         }
31027         // in theory this detects if the last element is not a br, then we try and do that.
31028         // its so clicking in space at bottom triggers adding a br and moving the cursor.
31029         if (e &&
31030             e.target.nodeName == 'BODY' &&
31031             e.type == "mouseup" &&
31032             this.doc.body.lastChild
31033            ) {
31034             var lc = this.doc.body.lastChild;
31035             // gtx-trans is google translate plugin adding crap.
31036             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31037                 lc = lc.previousSibling;
31038             }
31039             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31040             // if last element is <BR> - then dont do anything.
31041             
31042                 var ns = this.doc.createElement('br');
31043                 this.doc.body.appendChild(ns);
31044                 range = this.doc.createRange();
31045                 range.setStartAfter(ns);
31046                 range.collapse(true);
31047                 var sel = this.win.getSelection();
31048                 sel.removeAllRanges();
31049                 sel.addRange(range);
31050             }
31051         }
31052         
31053         
31054         
31055         this.fireEditorEvent(e);
31056       //  this.updateToolbar();
31057         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31058     },
31059     
31060     fireEditorEvent: function(e)
31061     {
31062         this.owner.fireEvent('editorevent', this, e);
31063     },
31064
31065     insertTag : function(tg)
31066     {
31067         // could be a bit smarter... -> wrap the current selected tRoo..
31068         if (tg.toLowerCase() == 'span' ||
31069             tg.toLowerCase() == 'code' ||
31070             tg.toLowerCase() == 'sup' ||
31071             tg.toLowerCase() == 'sub' 
31072             ) {
31073             
31074             range = this.createRange(this.getSelection());
31075             var wrappingNode = this.doc.createElement(tg.toLowerCase());
31076             wrappingNode.appendChild(range.extractContents());
31077             range.insertNode(wrappingNode);
31078
31079             return;
31080             
31081             
31082             
31083         }
31084         this.execCmd("formatblock",   tg);
31085         this.undoManager.addEvent(); 
31086     },
31087     
31088     insertText : function(txt)
31089     {
31090         
31091         
31092         var range = this.createRange();
31093         range.deleteContents();
31094                //alert(Sender.getAttribute('label'));
31095                
31096         range.insertNode(this.doc.createTextNode(txt));
31097         this.undoManager.addEvent();
31098     } ,
31099     
31100      
31101
31102     /**
31103      * Executes a Midas editor command on the editor document and performs necessary focus and
31104      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31105      * @param {String} cmd The Midas command
31106      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31107      */
31108     relayCmd : function(cmd, value)
31109     {
31110         
31111         switch (cmd) {
31112             case 'justifyleft':
31113             case 'justifyright':
31114             case 'justifycenter':
31115                 // if we are in a cell, then we will adjust the
31116                 var n = this.getParentElement();
31117                 var td = n.closest('td');
31118                 if (td) {
31119                     var bl = Roo.htmleditor.Block.factory(td);
31120                     bl.textAlign = cmd.replace('justify','');
31121                     bl.updateElement();
31122                     this.owner.fireEvent('editorevent', this);
31123                     return;
31124                 }
31125                 this.execCmd('styleWithCSS', true); // 
31126                 break;
31127             case 'bold':
31128             case 'italic':
31129                 // if there is no selection, then we insert, and set the curson inside it..
31130                 this.execCmd('styleWithCSS', false); 
31131                 break;
31132                 
31133         
31134             default:
31135                 break;
31136         }
31137         
31138         
31139         this.win.focus();
31140         this.execCmd(cmd, value);
31141         this.owner.fireEvent('editorevent', this);
31142         //this.updateToolbar();
31143         this.owner.deferFocus();
31144     },
31145
31146     /**
31147      * Executes a Midas editor command directly on the editor document.
31148      * For visual commands, you should use {@link #relayCmd} instead.
31149      * <b>This should only be called after the editor is initialized.</b>
31150      * @param {String} cmd The Midas command
31151      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31152      */
31153     execCmd : function(cmd, value){
31154         this.doc.execCommand(cmd, false, value === undefined ? null : value);
31155         this.syncValue();
31156     },
31157  
31158  
31159    
31160     /**
31161      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31162      * to insert tRoo.
31163      * @param {String} text | dom node.. 
31164      */
31165     insertAtCursor : function(text)
31166     {
31167         
31168         if(!this.activated){
31169             return;
31170         }
31171          
31172         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31173             this.win.focus();
31174             
31175             
31176             // from jquery ui (MIT licenced)
31177             var range, node;
31178             var win = this.win;
31179             
31180             if (win.getSelection && win.getSelection().getRangeAt) {
31181                 
31182                 // delete the existing?
31183                 
31184                 this.createRange(this.getSelection()).deleteContents();
31185                 range = win.getSelection().getRangeAt(0);
31186                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31187                 range.insertNode(node);
31188                 range = range.cloneRange();
31189                 range.collapse(false);
31190                  
31191                 win.getSelection().removeAllRanges();
31192                 win.getSelection().addRange(range);
31193                 
31194                 
31195                 
31196             } else if (win.document.selection && win.document.selection.createRange) {
31197                 // no firefox support
31198                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31199                 win.document.selection.createRange().pasteHTML(txt);
31200             
31201             } else {
31202                 // no firefox support
31203                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31204                 this.execCmd('InsertHTML', txt);
31205             } 
31206             this.syncValue();
31207             
31208             this.deferFocus();
31209         }
31210     },
31211  // private
31212     mozKeyPress : function(e){
31213         if(e.ctrlKey){
31214             var c = e.getCharCode(), cmd;
31215           
31216             if(c > 0){
31217                 c = String.fromCharCode(c).toLowerCase();
31218                 switch(c){
31219                     case 'b':
31220                         cmd = 'bold';
31221                         break;
31222                     case 'i':
31223                         cmd = 'italic';
31224                         break;
31225                     
31226                     case 'u':
31227                         cmd = 'underline';
31228                         break;
31229                     
31230                     //case 'v':
31231                       //  this.cleanUpPaste.defer(100, this);
31232                       //  return;
31233                         
31234                 }
31235                 if(cmd){
31236                     
31237                     this.relayCmd(cmd);
31238                     //this.win.focus();
31239                     //this.execCmd(cmd);
31240                     //this.deferFocus();
31241                     e.preventDefault();
31242                 }
31243                 
31244             }
31245         }
31246     },
31247
31248     // private
31249     fixKeys : function(){ // load time branching for fastest keydown performance
31250         
31251         
31252         if(Roo.isIE){
31253             return function(e){
31254                 var k = e.getKey(), r;
31255                 if(k == e.TAB){
31256                     e.stopEvent();
31257                     r = this.doc.selection.createRange();
31258                     if(r){
31259                         r.collapse(true);
31260                         r.pasteHTML('&#160;&#160;&#160;&#160;');
31261                         this.deferFocus();
31262                     }
31263                     return;
31264                 }
31265                 /// this is handled by Roo.htmleditor.KeyEnter
31266                  /*
31267                 if(k == e.ENTER){
31268                     r = this.doc.selection.createRange();
31269                     if(r){
31270                         var target = r.parentElement();
31271                         if(!target || target.tagName.toLowerCase() != 'li'){
31272                             e.stopEvent();
31273                             r.pasteHTML('<br/>');
31274                             r.collapse(false);
31275                             r.select();
31276                         }
31277                     }
31278                 }
31279                 */
31280                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31281                 //    this.cleanUpPaste.defer(100, this);
31282                 //    return;
31283                 //}
31284                 
31285                 
31286             };
31287         }else if(Roo.isOpera){
31288             return function(e){
31289                 var k = e.getKey();
31290                 if(k == e.TAB){
31291                     e.stopEvent();
31292                     this.win.focus();
31293                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
31294                     this.deferFocus();
31295                 }
31296                
31297                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31298                 //    this.cleanUpPaste.defer(100, this);
31299                  //   return;
31300                 //}
31301                 
31302             };
31303         }else if(Roo.isSafari){
31304             return function(e){
31305                 var k = e.getKey();
31306                 
31307                 if(k == e.TAB){
31308                     e.stopEvent();
31309                     this.execCmd('InsertText','\t');
31310                     this.deferFocus();
31311                     return;
31312                 }
31313                  this.mozKeyPress(e);
31314                 
31315                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31316                  //   this.cleanUpPaste.defer(100, this);
31317                  //   return;
31318                // }
31319                 
31320              };
31321         }
31322     }(),
31323     
31324     getAllAncestors: function()
31325     {
31326         var p = this.getSelectedNode();
31327         var a = [];
31328         if (!p) {
31329             a.push(p); // push blank onto stack..
31330             p = this.getParentElement();
31331         }
31332         
31333         
31334         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31335             a.push(p);
31336             p = p.parentNode;
31337         }
31338         a.push(this.doc.body);
31339         return a;
31340     },
31341     lastSel : false,
31342     lastSelNode : false,
31343     
31344     
31345     getSelection : function() 
31346     {
31347         this.assignDocWin();
31348         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31349     },
31350     /**
31351      * Select a dom node
31352      * @param {DomElement} node the node to select
31353      */
31354     selectNode : function(node, collapse)
31355     {
31356         var nodeRange = node.ownerDocument.createRange();
31357         try {
31358             nodeRange.selectNode(node);
31359         } catch (e) {
31360             nodeRange.selectNodeContents(node);
31361         }
31362         if (collapse === true) {
31363             nodeRange.collapse(true);
31364         }
31365         //
31366         var s = this.win.getSelection();
31367         s.removeAllRanges();
31368         s.addRange(nodeRange);
31369     },
31370     
31371     getSelectedNode: function() 
31372     {
31373         // this may only work on Gecko!!!
31374         
31375         // should we cache this!!!!
31376         
31377          
31378          
31379         var range = this.createRange(this.getSelection()).cloneRange();
31380         
31381         if (Roo.isIE) {
31382             var parent = range.parentElement();
31383             while (true) {
31384                 var testRange = range.duplicate();
31385                 testRange.moveToElementText(parent);
31386                 if (testRange.inRange(range)) {
31387                     break;
31388                 }
31389                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31390                     break;
31391                 }
31392                 parent = parent.parentElement;
31393             }
31394             return parent;
31395         }
31396         
31397         // is ancestor a text element.
31398         var ac =  range.commonAncestorContainer;
31399         if (ac.nodeType == 3) {
31400             ac = ac.parentNode;
31401         }
31402         
31403         var ar = ac.childNodes;
31404          
31405         var nodes = [];
31406         var other_nodes = [];
31407         var has_other_nodes = false;
31408         for (var i=0;i<ar.length;i++) {
31409             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
31410                 continue;
31411             }
31412             // fullly contained node.
31413             
31414             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31415                 nodes.push(ar[i]);
31416                 continue;
31417             }
31418             
31419             // probably selected..
31420             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31421                 other_nodes.push(ar[i]);
31422                 continue;
31423             }
31424             // outer..
31425             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
31426                 continue;
31427             }
31428             
31429             
31430             has_other_nodes = true;
31431         }
31432         if (!nodes.length && other_nodes.length) {
31433             nodes= other_nodes;
31434         }
31435         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
31436             return false;
31437         }
31438         
31439         return nodes[0];
31440     },
31441     
31442     
31443     createRange: function(sel)
31444     {
31445         // this has strange effects when using with 
31446         // top toolbar - not sure if it's a great idea.
31447         //this.editor.contentWindow.focus();
31448         if (typeof sel != "undefined") {
31449             try {
31450                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
31451             } catch(e) {
31452                 return this.doc.createRange();
31453             }
31454         } else {
31455             return this.doc.createRange();
31456         }
31457     },
31458     getParentElement: function()
31459     {
31460         
31461         this.assignDocWin();
31462         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
31463         
31464         var range = this.createRange(sel);
31465          
31466         try {
31467             var p = range.commonAncestorContainer;
31468             while (p.nodeType == 3) { // text node
31469                 p = p.parentNode;
31470             }
31471             return p;
31472         } catch (e) {
31473             return null;
31474         }
31475     
31476     },
31477     /***
31478      *
31479      * Range intersection.. the hard stuff...
31480      *  '-1' = before
31481      *  '0' = hits..
31482      *  '1' = after.
31483      *         [ -- selected range --- ]
31484      *   [fail]                        [fail]
31485      *
31486      *    basically..
31487      *      if end is before start or  hits it. fail.
31488      *      if start is after end or hits it fail.
31489      *
31490      *   if either hits (but other is outside. - then it's not 
31491      *   
31492      *    
31493      **/
31494     
31495     
31496     // @see http://www.thismuchiknow.co.uk/?p=64.
31497     rangeIntersectsNode : function(range, node)
31498     {
31499         var nodeRange = node.ownerDocument.createRange();
31500         try {
31501             nodeRange.selectNode(node);
31502         } catch (e) {
31503             nodeRange.selectNodeContents(node);
31504         }
31505     
31506         var rangeStartRange = range.cloneRange();
31507         rangeStartRange.collapse(true);
31508     
31509         var rangeEndRange = range.cloneRange();
31510         rangeEndRange.collapse(false);
31511     
31512         var nodeStartRange = nodeRange.cloneRange();
31513         nodeStartRange.collapse(true);
31514     
31515         var nodeEndRange = nodeRange.cloneRange();
31516         nodeEndRange.collapse(false);
31517     
31518         return rangeStartRange.compareBoundaryPoints(
31519                  Range.START_TO_START, nodeEndRange) == -1 &&
31520                rangeEndRange.compareBoundaryPoints(
31521                  Range.START_TO_START, nodeStartRange) == 1;
31522         
31523          
31524     },
31525     rangeCompareNode : function(range, node)
31526     {
31527         var nodeRange = node.ownerDocument.createRange();
31528         try {
31529             nodeRange.selectNode(node);
31530         } catch (e) {
31531             nodeRange.selectNodeContents(node);
31532         }
31533         
31534         
31535         range.collapse(true);
31536     
31537         nodeRange.collapse(true);
31538      
31539         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
31540         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
31541          
31542         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
31543         
31544         var nodeIsBefore   =  ss == 1;
31545         var nodeIsAfter    = ee == -1;
31546         
31547         if (nodeIsBefore && nodeIsAfter) {
31548             return 0; // outer
31549         }
31550         if (!nodeIsBefore && nodeIsAfter) {
31551             return 1; //right trailed.
31552         }
31553         
31554         if (nodeIsBefore && !nodeIsAfter) {
31555             return 2;  // left trailed.
31556         }
31557         // fully contined.
31558         return 3;
31559     },
31560  
31561     cleanWordChars : function(input) {// change the chars to hex code
31562         
31563        var swapCodes  = [ 
31564             [    8211, "&#8211;" ], 
31565             [    8212, "&#8212;" ], 
31566             [    8216,  "'" ],  
31567             [    8217, "'" ],  
31568             [    8220, '"' ],  
31569             [    8221, '"' ],  
31570             [    8226, "*" ],  
31571             [    8230, "..." ]
31572         ]; 
31573         var output = input;
31574         Roo.each(swapCodes, function(sw) { 
31575             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
31576             
31577             output = output.replace(swapper, sw[1]);
31578         });
31579         
31580         return output;
31581     },
31582     
31583      
31584     
31585         
31586     
31587     cleanUpChild : function (node)
31588     {
31589         
31590         new Roo.htmleditor.FilterComment({node : node});
31591         new Roo.htmleditor.FilterAttributes({
31592                 node : node,
31593                 attrib_black : this.ablack,
31594                 attrib_clean : this.aclean,
31595                 style_white : this.cwhite,
31596                 style_black : this.cblack
31597         });
31598         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
31599         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
31600          
31601         
31602     },
31603     
31604     /**
31605      * Clean up MS wordisms...
31606      * @deprecated - use filter directly
31607      */
31608     cleanWord : function(node)
31609     {
31610         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
31611         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
31612         
31613     },
31614    
31615     
31616     /**
31617
31618      * @deprecated - use filters
31619      */
31620     cleanTableWidths : function(node)
31621     {
31622         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
31623         
31624  
31625     },
31626     
31627      
31628         
31629     applyBlacklists : function()
31630     {
31631         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
31632         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
31633         
31634         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
31635         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
31636         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
31637         
31638         this.white = [];
31639         this.black = [];
31640         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
31641             if (b.indexOf(tag) > -1) {
31642                 return;
31643             }
31644             this.white.push(tag);
31645             
31646         }, this);
31647         
31648         Roo.each(w, function(tag) {
31649             if (b.indexOf(tag) > -1) {
31650                 return;
31651             }
31652             if (this.white.indexOf(tag) > -1) {
31653                 return;
31654             }
31655             this.white.push(tag);
31656             
31657         }, this);
31658         
31659         
31660         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
31661             if (w.indexOf(tag) > -1) {
31662                 return;
31663             }
31664             this.black.push(tag);
31665             
31666         }, this);
31667         
31668         Roo.each(b, function(tag) {
31669             if (w.indexOf(tag) > -1) {
31670                 return;
31671             }
31672             if (this.black.indexOf(tag) > -1) {
31673                 return;
31674             }
31675             this.black.push(tag);
31676             
31677         }, this);
31678         
31679         
31680         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
31681         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
31682         
31683         this.cwhite = [];
31684         this.cblack = [];
31685         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
31686             if (b.indexOf(tag) > -1) {
31687                 return;
31688             }
31689             this.cwhite.push(tag);
31690             
31691         }, this);
31692         
31693         Roo.each(w, function(tag) {
31694             if (b.indexOf(tag) > -1) {
31695                 return;
31696             }
31697             if (this.cwhite.indexOf(tag) > -1) {
31698                 return;
31699             }
31700             this.cwhite.push(tag);
31701             
31702         }, this);
31703         
31704         
31705         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
31706             if (w.indexOf(tag) > -1) {
31707                 return;
31708             }
31709             this.cblack.push(tag);
31710             
31711         }, this);
31712         
31713         Roo.each(b, function(tag) {
31714             if (w.indexOf(tag) > -1) {
31715                 return;
31716             }
31717             if (this.cblack.indexOf(tag) > -1) {
31718                 return;
31719             }
31720             this.cblack.push(tag);
31721             
31722         }, this);
31723     },
31724     
31725     setStylesheets : function(stylesheets)
31726     {
31727         if(typeof(stylesheets) == 'string'){
31728             Roo.get(this.iframe.contentDocument.head).createChild({
31729                 tag : 'link',
31730                 rel : 'stylesheet',
31731                 type : 'text/css',
31732                 href : stylesheets
31733             });
31734             
31735             return;
31736         }
31737         var _this = this;
31738      
31739         Roo.each(stylesheets, function(s) {
31740             if(!s.length){
31741                 return;
31742             }
31743             
31744             Roo.get(_this.iframe.contentDocument.head).createChild({
31745                 tag : 'link',
31746                 rel : 'stylesheet',
31747                 type : 'text/css',
31748                 href : s
31749             });
31750         });
31751
31752         
31753     },
31754     
31755     
31756     updateLanguage : function()
31757     {
31758         if (!this.iframe || !this.iframe.contentDocument) {
31759             return;
31760         }
31761         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
31762     },
31763     
31764     
31765     removeStylesheets : function()
31766     {
31767         var _this = this;
31768         
31769         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
31770             s.remove();
31771         });
31772     },
31773     
31774     setStyle : function(style)
31775     {
31776         Roo.get(this.iframe.contentDocument.head).createChild({
31777             tag : 'style',
31778             type : 'text/css',
31779             html : style
31780         });
31781
31782         return;
31783     }
31784     
31785     // hide stuff that is not compatible
31786     /**
31787      * @event blur
31788      * @hide
31789      */
31790     /**
31791      * @event change
31792      * @hide
31793      */
31794     /**
31795      * @event focus
31796      * @hide
31797      */
31798     /**
31799      * @event specialkey
31800      * @hide
31801      */
31802     /**
31803      * @cfg {String} fieldClass @hide
31804      */
31805     /**
31806      * @cfg {String} focusClass @hide
31807      */
31808     /**
31809      * @cfg {String} autoCreate @hide
31810      */
31811     /**
31812      * @cfg {String} inputType @hide
31813      */
31814     /**
31815      * @cfg {String} invalidClass @hide
31816      */
31817     /**
31818      * @cfg {String} invalidText @hide
31819      */
31820     /**
31821      * @cfg {String} msgFx @hide
31822      */
31823     /**
31824      * @cfg {String} validateOnBlur @hide
31825      */
31826 });
31827
31828 Roo.HtmlEditorCore.white = [
31829         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
31830         
31831        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
31832        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
31833        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
31834        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
31835        'TABLE',   'UL',         'XMP', 
31836        
31837        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
31838       'THEAD',   'TR', 
31839      
31840       'DIR', 'MENU', 'OL', 'UL', 'DL',
31841        
31842       'EMBED',  'OBJECT'
31843 ];
31844
31845
31846 Roo.HtmlEditorCore.black = [
31847     //    'embed',  'object', // enable - backend responsiblity to clean thiese
31848         'APPLET', // 
31849         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
31850         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
31851         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
31852         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
31853         //'FONT' // CLEAN LATER..
31854         'COLGROUP', 'COL'   // messy tables.
31855         
31856         
31857 ];
31858 Roo.HtmlEditorCore.clean = [ // ?? needed???
31859      'SCRIPT', 'STYLE', 'TITLE', 'XML'
31860 ];
31861 Roo.HtmlEditorCore.tag_remove = [
31862     'FONT', 'TBODY'  
31863 ];
31864 // attributes..
31865
31866 Roo.HtmlEditorCore.ablack = [
31867     'on'
31868 ];
31869     
31870 Roo.HtmlEditorCore.aclean = [ 
31871     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
31872 ];
31873
31874 // protocols..
31875 Roo.HtmlEditorCore.pwhite= [
31876         'http',  'https',  'mailto'
31877 ];
31878
31879 // white listed style attributes.
31880 Roo.HtmlEditorCore.cwhite= [
31881       //  'text-align', /// default is to allow most things..
31882       
31883          
31884 //        'font-size'//??
31885 ];
31886
31887 // black listed style attributes.
31888 Roo.HtmlEditorCore.cblack= [
31889       //  'font-size' -- this can be set by the project 
31890 ];
31891
31892
31893
31894
31895     /*
31896  * - LGPL
31897  *
31898  * HtmlEditor
31899  * 
31900  */
31901
31902 /**
31903  * @class Roo.bootstrap.form.HtmlEditor
31904  * @extends Roo.bootstrap.form.TextArea
31905  * Bootstrap HtmlEditor class
31906
31907  * @constructor
31908  * Create a new HtmlEditor
31909  * @param {Object} config The config object
31910  */
31911
31912 Roo.bootstrap.form.HtmlEditor = function(config){
31913     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
31914     if (!this.toolbars) {
31915         this.toolbars = [];
31916     }
31917     
31918     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
31919     this.addEvents({
31920             /**
31921              * @event initialize
31922              * Fires when the editor is fully initialized (including the iframe)
31923              * @param {HtmlEditor} this
31924              */
31925             initialize: true,
31926             /**
31927              * @event activate
31928              * Fires when the editor is first receives the focus. Any insertion must wait
31929              * until after this event.
31930              * @param {HtmlEditor} this
31931              */
31932             activate: true,
31933              /**
31934              * @event beforesync
31935              * Fires before the textarea is updated with content from the editor iframe. Return false
31936              * to cancel the sync.
31937              * @param {HtmlEditor} this
31938              * @param {String} html
31939              */
31940             beforesync: true,
31941              /**
31942              * @event beforepush
31943              * Fires before the iframe editor is updated with content from the textarea. Return false
31944              * to cancel the push.
31945              * @param {HtmlEditor} this
31946              * @param {String} html
31947              */
31948             beforepush: true,
31949              /**
31950              * @event sync
31951              * Fires when the textarea is updated with content from the editor iframe.
31952              * @param {HtmlEditor} this
31953              * @param {String} html
31954              */
31955             sync: true,
31956              /**
31957              * @event push
31958              * Fires when the iframe editor is updated with content from the textarea.
31959              * @param {HtmlEditor} this
31960              * @param {String} html
31961              */
31962             push: true,
31963              /**
31964              * @event editmodechange
31965              * Fires when the editor switches edit modes
31966              * @param {HtmlEditor} this
31967              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
31968              */
31969             editmodechange: true,
31970             /**
31971              * @event editorevent
31972              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
31973              * @param {HtmlEditor} this
31974              */
31975             editorevent: true,
31976             /**
31977              * @event firstfocus
31978              * Fires when on first focus - needed by toolbars..
31979              * @param {HtmlEditor} this
31980              */
31981             firstfocus: true,
31982             /**
31983              * @event autosave
31984              * Auto save the htmlEditor value as a file into Events
31985              * @param {HtmlEditor} this
31986              */
31987             autosave: true,
31988             /**
31989              * @event savedpreview
31990              * preview the saved version of htmlEditor
31991              * @param {HtmlEditor} this
31992              */
31993             savedpreview: true
31994         });
31995 };
31996
31997
31998 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
31999     
32000     
32001       /**
32002      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
32003      */
32004     toolbars : false,
32005     
32006      /**
32007     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32008     */
32009     btns : [],
32010    
32011      /**
32012      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
32013      *                        Roo.resizable.
32014      */
32015     resizable : false,
32016      /**
32017      * @cfg {Number} height (in pixels)
32018      */   
32019     height: 300,
32020    /**
32021      * @cfg {Number} width (in pixels)
32022      */   
32023     width: false,
32024     
32025     /**
32026      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32027      * 
32028      */
32029     stylesheets: false,
32030     
32031     // id of frame..
32032     frameId: false,
32033     
32034     // private properties
32035     validationEvent : false,
32036     deferHeight: true,
32037     initialized : false,
32038     activated : false,
32039     
32040     onFocus : Roo.emptyFn,
32041     iframePad:3,
32042     hideMode:'offsets',
32043     
32044     tbContainer : false,
32045     
32046     bodyCls : '',
32047     
32048     toolbarContainer :function() {
32049         return this.wrap.select('.x-html-editor-tb',true).first();
32050     },
32051
32052     /**
32053      * Protected method that will not generally be called directly. It
32054      * is called when the editor creates its toolbar. Override this method if you need to
32055      * add custom toolbar buttons.
32056      * @param {HtmlEditor} editor
32057      */
32058     createToolbar : function(){
32059         Roo.log('renewing');
32060         Roo.log("create toolbars");
32061         
32062         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
32063         this.toolbars[0].render(this.toolbarContainer());
32064         
32065         return;
32066         
32067 //        if (!editor.toolbars || !editor.toolbars.length) {
32068 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
32069 //        }
32070 //        
32071 //        for (var i =0 ; i < editor.toolbars.length;i++) {
32072 //            editor.toolbars[i] = Roo.factory(
32073 //                    typeof(editor.toolbars[i]) == 'string' ?
32074 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
32075 //                Roo.bootstrap.form.HtmlEditor);
32076 //            editor.toolbars[i].init(editor);
32077 //        }
32078     },
32079
32080      
32081     // private
32082     onRender : function(ct, position)
32083     {
32084        // Roo.log("Call onRender: " + this.xtype);
32085         var _t = this;
32086         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32087       
32088         this.wrap = this.inputEl().wrap({
32089             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32090         });
32091         
32092         this.editorcore.onRender(ct, position);
32093          
32094         if (this.resizable) {
32095             this.resizeEl = new Roo.Resizable(this.wrap, {
32096                 pinned : true,
32097                 wrap: true,
32098                 dynamic : true,
32099                 minHeight : this.height,
32100                 height: this.height,
32101                 handles : this.resizable,
32102                 width: this.width,
32103                 listeners : {
32104                     resize : function(r, w, h) {
32105                         _t.onResize(w,h); // -something
32106                     }
32107                 }
32108             });
32109             
32110         }
32111         this.createToolbar(this);
32112        
32113         
32114         if(!this.width && this.resizable){
32115             this.setSize(this.wrap.getSize());
32116         }
32117         if (this.resizeEl) {
32118             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
32119             // should trigger onReize..
32120         }
32121         
32122     },
32123
32124     // private
32125     onResize : function(w, h)
32126     {
32127         Roo.log('resize: ' +w + ',' + h );
32128         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32129         var ew = false;
32130         var eh = false;
32131         
32132         if(this.inputEl() ){
32133             if(typeof w == 'number'){
32134                 var aw = w - this.wrap.getFrameWidth('lr');
32135                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32136                 ew = aw;
32137             }
32138             if(typeof h == 'number'){
32139                  var tbh = -11;  // fixme it needs to tool bar size!
32140                 for (var i =0; i < this.toolbars.length;i++) {
32141                     // fixme - ask toolbars for heights?
32142                     tbh += this.toolbars[i].el.getHeight();
32143                     //if (this.toolbars[i].footer) {
32144                     //    tbh += this.toolbars[i].footer.el.getHeight();
32145                     //}
32146                 }
32147               
32148                 
32149                 
32150                 
32151                 
32152                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32153                 ah -= 5; // knock a few pixes off for look..
32154                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32155                 var eh = ah;
32156             }
32157         }
32158         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32159         this.editorcore.onResize(ew,eh);
32160         
32161     },
32162
32163     /**
32164      * Toggles the editor between standard and source edit mode.
32165      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32166      */
32167     toggleSourceEdit : function(sourceEditMode)
32168     {
32169         this.editorcore.toggleSourceEdit(sourceEditMode);
32170         
32171         if(this.editorcore.sourceEditMode){
32172             Roo.log('editor - showing textarea');
32173             
32174 //            Roo.log('in');
32175 //            Roo.log(this.syncValue());
32176             this.syncValue();
32177             this.inputEl().removeClass(['hide', 'x-hidden']);
32178             this.inputEl().dom.removeAttribute('tabIndex');
32179             this.inputEl().focus();
32180         }else{
32181             Roo.log('editor - hiding textarea');
32182 //            Roo.log('out')
32183 //            Roo.log(this.pushValue()); 
32184             this.pushValue();
32185             
32186             this.inputEl().addClass(['hide', 'x-hidden']);
32187             this.inputEl().dom.setAttribute('tabIndex', -1);
32188             //this.deferFocus();
32189         }
32190          
32191         if(this.resizable){
32192             this.setSize(this.wrap.getSize());
32193         }
32194         
32195         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32196     },
32197  
32198     // private (for BoxComponent)
32199     adjustSize : Roo.BoxComponent.prototype.adjustSize,
32200
32201     // private (for BoxComponent)
32202     getResizeEl : function(){
32203         return this.wrap;
32204     },
32205
32206     // private (for BoxComponent)
32207     getPositionEl : function(){
32208         return this.wrap;
32209     },
32210
32211     // private
32212     initEvents : function(){
32213         this.originalValue = this.getValue();
32214     },
32215
32216 //    /**
32217 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32218 //     * @method
32219 //     */
32220 //    markInvalid : Roo.emptyFn,
32221 //    /**
32222 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32223 //     * @method
32224 //     */
32225 //    clearInvalid : Roo.emptyFn,
32226
32227     setValue : function(v){
32228         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32229         this.editorcore.pushValue();
32230     },
32231
32232      
32233     // private
32234     deferFocus : function(){
32235         this.focus.defer(10, this);
32236     },
32237
32238     // doc'ed in Field
32239     focus : function(){
32240         this.editorcore.focus();
32241         
32242     },
32243       
32244
32245     // private
32246     onDestroy : function(){
32247         
32248         
32249         
32250         if(this.rendered){
32251             
32252             for (var i =0; i < this.toolbars.length;i++) {
32253                 // fixme - ask toolbars for heights?
32254                 this.toolbars[i].onDestroy();
32255             }
32256             
32257             this.wrap.dom.innerHTML = '';
32258             this.wrap.remove();
32259         }
32260     },
32261
32262     // private
32263     onFirstFocus : function(){
32264         //Roo.log("onFirstFocus");
32265         this.editorcore.onFirstFocus();
32266          for (var i =0; i < this.toolbars.length;i++) {
32267             this.toolbars[i].onFirstFocus();
32268         }
32269         
32270     },
32271     
32272     // private
32273     syncValue : function()
32274     {   
32275         this.editorcore.syncValue();
32276     },
32277     
32278     pushValue : function()
32279     {   
32280         this.editorcore.pushValue();
32281     }
32282      
32283     
32284     // hide stuff that is not compatible
32285     /**
32286      * @event blur
32287      * @hide
32288      */
32289     /**
32290      * @event change
32291      * @hide
32292      */
32293     /**
32294      * @event focus
32295      * @hide
32296      */
32297     /**
32298      * @event specialkey
32299      * @hide
32300      */
32301     /**
32302      * @cfg {String} fieldClass @hide
32303      */
32304     /**
32305      * @cfg {String} focusClass @hide
32306      */
32307     /**
32308      * @cfg {String} autoCreate @hide
32309      */
32310     /**
32311      * @cfg {String} inputType @hide
32312      */
32313      
32314     /**
32315      * @cfg {String} invalidText @hide
32316      */
32317     /**
32318      * @cfg {String} msgFx @hide
32319      */
32320     /**
32321      * @cfg {String} validateOnBlur @hide
32322      */
32323 });
32324  
32325     
32326    
32327    
32328    
32329       
32330 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
32331 /**
32332  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
32333  * @parent Roo.bootstrap.form.HtmlEditor
32334  * @extends Roo.bootstrap.nav.Simplebar
32335  * Basic Toolbar
32336  * 
32337  * @example
32338  * Usage:
32339  *
32340  new Roo.bootstrap.form.HtmlEditor({
32341     ....
32342     toolbars : [
32343         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
32344             disable : { fonts: 1 , format: 1, ..., ... , ...],
32345             btns : [ .... ]
32346         })
32347     }
32348      
32349  * 
32350  * @cfg {Object} disable List of elements to disable..
32351  * @cfg {Array} btns List of additional buttons.
32352  * 
32353  * 
32354  * NEEDS Extra CSS? 
32355  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32356  */
32357  
32358 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
32359 {
32360     
32361     Roo.apply(this, config);
32362     
32363     // default disabled, based on 'good practice'..
32364     this.disable = this.disable || {};
32365     Roo.applyIf(this.disable, {
32366         fontSize : true,
32367         colors : true,
32368         specialElements : true
32369     });
32370     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
32371     
32372     this.editor = config.editor;
32373     this.editorcore = config.editor.editorcore;
32374     
32375     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
32376     
32377     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32378     // dont call parent... till later.
32379 }
32380 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
32381      
32382     bar : true,
32383     
32384     editor : false,
32385     editorcore : false,
32386     
32387     
32388     formats : [
32389         "p" ,  
32390         "h1","h2","h3","h4","h5","h6", 
32391         "pre", "code", 
32392         "abbr", "acronym", "address", "cite", "samp", "var",
32393         'div','span'
32394     ],
32395     
32396     onRender : function(ct, position)
32397     {
32398        // Roo.log("Call onRender: " + this.xtype);
32399         
32400        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
32401        Roo.log(this.el);
32402        this.el.dom.style.marginBottom = '0';
32403        var _this = this;
32404        var editorcore = this.editorcore;
32405        var editor= this.editor;
32406        
32407        var children = [];
32408        var btn = function(id,cmd , toggle, handler, html){
32409        
32410             var  event = toggle ? 'toggle' : 'click';
32411        
32412             var a = {
32413                 size : 'sm',
32414                 xtype: 'Button',
32415                 xns: Roo.bootstrap,
32416                 //glyphicon : id,
32417                 fa: id,
32418                 cmd : id || cmd,
32419                 enableToggle:toggle !== false,
32420                 html : html || '',
32421                 pressed : toggle ? false : null,
32422                 listeners : {}
32423             };
32424             a.listeners[toggle ? 'toggle' : 'click'] = function() {
32425                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
32426             };
32427             children.push(a);
32428             return a;
32429        }
32430        
32431     //    var cb_box = function...
32432         
32433         var style = {
32434                 xtype: 'Button',
32435                 size : 'sm',
32436                 xns: Roo.bootstrap,
32437                 fa : 'font',
32438                 //html : 'submit'
32439                 menu : {
32440                     xtype: 'Menu',
32441                     xns: Roo.bootstrap,
32442                     items:  []
32443                 }
32444         };
32445         Roo.each(this.formats, function(f) {
32446             style.menu.items.push({
32447                 xtype :'MenuItem',
32448                 xns: Roo.bootstrap,
32449                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
32450                 tagname : f,
32451                 listeners : {
32452                     click : function()
32453                     {
32454                         editorcore.insertTag(this.tagname);
32455                         editor.focus();
32456                     }
32457                 }
32458                 
32459             });
32460         });
32461         children.push(style);   
32462         
32463         btn('bold',false,true);
32464         btn('italic',false,true);
32465         btn('align-left', 'justifyleft',true);
32466         btn('align-center', 'justifycenter',true);
32467         btn('align-right' , 'justifyright',true);
32468         btn('link', false, false, function(btn) {
32469             //Roo.log("create link?");
32470             var url = prompt(this.createLinkText, this.defaultLinkValue);
32471             if(url && url != 'http:/'+'/'){
32472                 this.editorcore.relayCmd('createlink', url);
32473             }
32474         }),
32475         btn('list','insertunorderedlist',true);
32476         btn('pencil', false,true, function(btn){
32477                 Roo.log(this);
32478                 this.toggleSourceEdit(btn.pressed);
32479         });
32480         
32481         if (this.editor.btns.length > 0) {
32482             for (var i = 0; i<this.editor.btns.length; i++) {
32483                 children.push(this.editor.btns[i]);
32484             }
32485         }
32486         
32487         /*
32488         var cog = {
32489                 xtype: 'Button',
32490                 size : 'sm',
32491                 xns: Roo.bootstrap,
32492                 glyphicon : 'cog',
32493                 //html : 'submit'
32494                 menu : {
32495                     xtype: 'Menu',
32496                     xns: Roo.bootstrap,
32497                     items:  []
32498                 }
32499         };
32500         
32501         cog.menu.items.push({
32502             xtype :'MenuItem',
32503             xns: Roo.bootstrap,
32504             html : Clean styles,
32505             tagname : f,
32506             listeners : {
32507                 click : function()
32508                 {
32509                     editorcore.insertTag(this.tagname);
32510                     editor.focus();
32511                 }
32512             }
32513             
32514         });
32515        */
32516         
32517          
32518        this.xtype = 'NavSimplebar';
32519         
32520         for(var i=0;i< children.length;i++) {
32521             
32522             this.buttons.add(this.addxtypeChild(children[i]));
32523             
32524         }
32525         
32526         editor.on('editorevent', this.updateToolbar, this);
32527     },
32528     onBtnClick : function(id)
32529     {
32530        this.editorcore.relayCmd(id);
32531        this.editorcore.focus();
32532     },
32533     
32534     /**
32535      * Protected method that will not generally be called directly. It triggers
32536      * a toolbar update by reading the markup state of the current selection in the editor.
32537      */
32538     updateToolbar: function(){
32539
32540         if(!this.editorcore.activated){
32541             this.editor.onFirstFocus(); // is this neeed?
32542             return;
32543         }
32544
32545         var btns = this.buttons; 
32546         var doc = this.editorcore.doc;
32547         btns.get('bold').setActive(doc.queryCommandState('bold'));
32548         btns.get('italic').setActive(doc.queryCommandState('italic'));
32549         //btns.get('underline').setActive(doc.queryCommandState('underline'));
32550         
32551         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
32552         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
32553         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
32554         
32555         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
32556         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
32557          /*
32558         
32559         var ans = this.editorcore.getAllAncestors();
32560         if (this.formatCombo) {
32561             
32562             
32563             var store = this.formatCombo.store;
32564             this.formatCombo.setValue("");
32565             for (var i =0; i < ans.length;i++) {
32566                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
32567                     // select it..
32568                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
32569                     break;
32570                 }
32571             }
32572         }
32573         
32574         
32575         
32576         // hides menus... - so this cant be on a menu...
32577         Roo.bootstrap.MenuMgr.hideAll();
32578         */
32579         Roo.bootstrap.menu.Manager.hideAll();
32580         //this.editorsyncValue();
32581     },
32582     onFirstFocus: function() {
32583         this.buttons.each(function(item){
32584            item.enable();
32585         });
32586     },
32587     toggleSourceEdit : function(sourceEditMode){
32588         
32589           
32590         if(sourceEditMode){
32591             Roo.log("disabling buttons");
32592            this.buttons.each( function(item){
32593                 if(item.cmd != 'pencil'){
32594                     item.disable();
32595                 }
32596             });
32597           
32598         }else{
32599             Roo.log("enabling buttons");
32600             if(this.editorcore.initialized){
32601                 this.buttons.each( function(item){
32602                     item.enable();
32603                 });
32604             }
32605             
32606         }
32607         Roo.log("calling toggole on editor");
32608         // tell the editor that it's been pressed..
32609         this.editor.toggleSourceEdit(sourceEditMode);
32610        
32611     }
32612 });
32613
32614
32615
32616
32617  
32618 /*
32619  * - LGPL
32620  */
32621
32622 /**
32623  * @class Roo.bootstrap.form.Markdown
32624  * @extends Roo.bootstrap.form.TextArea
32625  * Bootstrap Showdown editable area
32626  * @cfg {string} content
32627  * 
32628  * @constructor
32629  * Create a new Showdown
32630  */
32631
32632 Roo.bootstrap.form.Markdown = function(config){
32633     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
32634    
32635 };
32636
32637 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
32638     
32639     editing :false,
32640     
32641     initEvents : function()
32642     {
32643         
32644         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
32645         this.markdownEl = this.el.createChild({
32646             cls : 'roo-markdown-area'
32647         });
32648         this.inputEl().addClass('d-none');
32649         if (this.getValue() == '') {
32650             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
32651             
32652         } else {
32653             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
32654         }
32655         this.markdownEl.on('click', this.toggleTextEdit, this);
32656         this.on('blur', this.toggleTextEdit, this);
32657         this.on('specialkey', this.resizeTextArea, this);
32658     },
32659     
32660     toggleTextEdit : function()
32661     {
32662         var sh = this.markdownEl.getHeight();
32663         this.inputEl().addClass('d-none');
32664         this.markdownEl.addClass('d-none');
32665         if (!this.editing) {
32666             // show editor?
32667             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
32668             this.inputEl().removeClass('d-none');
32669             this.inputEl().focus();
32670             this.editing = true;
32671             return;
32672         }
32673         // show showdown...
32674         this.updateMarkdown();
32675         this.markdownEl.removeClass('d-none');
32676         this.editing = false;
32677         return;
32678     },
32679     updateMarkdown : function()
32680     {
32681         if (this.getValue() == '') {
32682             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
32683             return;
32684         }
32685  
32686         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
32687     },
32688     
32689     resizeTextArea: function () {
32690         
32691         var sh = 100;
32692         Roo.log([sh, this.getValue().split("\n").length * 30]);
32693         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
32694     },
32695     setValue : function(val)
32696     {
32697         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
32698         if (!this.editing) {
32699             this.updateMarkdown();
32700         }
32701         
32702     },
32703     focus : function()
32704     {
32705         if (!this.editing) {
32706             this.toggleTextEdit();
32707         }
32708         
32709     }
32710
32711
32712 });/*
32713  * Based on:
32714  * Ext JS Library 1.1.1
32715  * Copyright(c) 2006-2007, Ext JS, LLC.
32716  *
32717  * Originally Released Under LGPL - original licence link has changed is not relivant.
32718  *
32719  * Fork - LGPL
32720  * <script type="text/javascript">
32721  */
32722  
32723 /**
32724  * @class Roo.bootstrap.PagingToolbar
32725  * @extends Roo.bootstrap.nav.Simplebar
32726  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
32727  * @constructor
32728  * Create a new PagingToolbar
32729  * @param {Object} config The config object
32730  * @param {Roo.data.Store} store
32731  */
32732 Roo.bootstrap.PagingToolbar = function(config)
32733 {
32734     // old args format still supported... - xtype is prefered..
32735         // created from xtype...
32736     
32737     this.ds = config.dataSource;
32738     
32739     if (config.store && !this.ds) {
32740         this.store= Roo.factory(config.store, Roo.data);
32741         this.ds = this.store;
32742         this.ds.xmodule = this.xmodule || false;
32743     }
32744     
32745     this.toolbarItems = [];
32746     if (config.items) {
32747         this.toolbarItems = config.items;
32748     }
32749     
32750     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
32751     
32752     this.cursor = 0;
32753     
32754     if (this.ds) { 
32755         this.bind(this.ds);
32756     }
32757     
32758     if (Roo.bootstrap.version == 4) {
32759         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
32760     } else {
32761         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
32762     }
32763     
32764 };
32765
32766 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
32767     /**
32768      * @cfg {Roo.bootstrap.Button} buttons[]
32769      * Buttons for the toolbar
32770      */
32771      /**
32772      * @cfg {Roo.data.Store} store
32773      * The underlying data store providing the paged data
32774      */
32775     /**
32776      * @cfg {String/HTMLElement/Element} container
32777      * container The id or element that will contain the toolbar
32778      */
32779     /**
32780      * @cfg {Boolean} displayInfo
32781      * True to display the displayMsg (defaults to false)
32782      */
32783     /**
32784      * @cfg {Number} pageSize
32785      * The number of records to display per page (defaults to 20)
32786      */
32787     pageSize: 20,
32788     /**
32789      * @cfg {String} displayMsg
32790      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32791      */
32792     displayMsg : 'Displaying {0} - {1} of {2}',
32793     /**
32794      * @cfg {String} emptyMsg
32795      * The message to display when no records are found (defaults to "No data to display")
32796      */
32797     emptyMsg : 'No data to display',
32798     /**
32799      * Customizable piece of the default paging text (defaults to "Page")
32800      * @type String
32801      */
32802     beforePageText : "Page",
32803     /**
32804      * Customizable piece of the default paging text (defaults to "of %0")
32805      * @type String
32806      */
32807     afterPageText : "of {0}",
32808     /**
32809      * Customizable piece of the default paging text (defaults to "First Page")
32810      * @type String
32811      */
32812     firstText : "First Page",
32813     /**
32814      * Customizable piece of the default paging text (defaults to "Previous Page")
32815      * @type String
32816      */
32817     prevText : "Previous Page",
32818     /**
32819      * Customizable piece of the default paging text (defaults to "Next Page")
32820      * @type String
32821      */
32822     nextText : "Next Page",
32823     /**
32824      * Customizable piece of the default paging text (defaults to "Last Page")
32825      * @type String
32826      */
32827     lastText : "Last Page",
32828     /**
32829      * Customizable piece of the default paging text (defaults to "Refresh")
32830      * @type String
32831      */
32832     refreshText : "Refresh",
32833
32834     buttons : false,
32835     // private
32836     onRender : function(ct, position) 
32837     {
32838         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
32839         this.navgroup.parentId = this.id;
32840         this.navgroup.onRender(this.el, null);
32841         // add the buttons to the navgroup
32842         
32843         if(this.displayInfo){
32844             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
32845             this.displayEl = this.el.select('.x-paging-info', true).first();
32846 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
32847 //            this.displayEl = navel.el.select('span',true).first();
32848         }
32849         
32850         var _this = this;
32851         
32852         if(this.buttons){
32853             Roo.each(_this.buttons, function(e){ // this might need to use render????
32854                Roo.factory(e).render(_this.el);
32855             });
32856         }
32857             
32858         Roo.each(_this.toolbarItems, function(e) {
32859             _this.navgroup.addItem(e);
32860         });
32861         
32862         
32863         this.first = this.navgroup.addItem({
32864             tooltip: this.firstText,
32865             cls: "prev btn-outline-secondary",
32866             html : ' <i class="fa fa-step-backward"></i>',
32867             disabled: true,
32868             preventDefault: true,
32869             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
32870         });
32871         
32872         this.prev =  this.navgroup.addItem({
32873             tooltip: this.prevText,
32874             cls: "prev btn-outline-secondary",
32875             html : ' <i class="fa fa-backward"></i>',
32876             disabled: true,
32877             preventDefault: true,
32878             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
32879         });
32880     //this.addSeparator();
32881         
32882         
32883         var field = this.navgroup.addItem( {
32884             tagtype : 'span',
32885             cls : 'x-paging-position  btn-outline-secondary',
32886              disabled: true,
32887             html : this.beforePageText  +
32888                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
32889                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
32890          } ); //?? escaped?
32891         
32892         this.field = field.el.select('input', true).first();
32893         this.field.on("keydown", this.onPagingKeydown, this);
32894         this.field.on("focus", function(){this.dom.select();});
32895     
32896     
32897         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
32898         //this.field.setHeight(18);
32899         //this.addSeparator();
32900         this.next = this.navgroup.addItem({
32901             tooltip: this.nextText,
32902             cls: "next btn-outline-secondary",
32903             html : ' <i class="fa fa-forward"></i>',
32904             disabled: true,
32905             preventDefault: true,
32906             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
32907         });
32908         this.last = this.navgroup.addItem({
32909             tooltip: this.lastText,
32910             html : ' <i class="fa fa-step-forward"></i>',
32911             cls: "next btn-outline-secondary",
32912             disabled: true,
32913             preventDefault: true,
32914             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
32915         });
32916     //this.addSeparator();
32917         this.loading = this.navgroup.addItem({
32918             tooltip: this.refreshText,
32919             cls: "btn-outline-secondary",
32920             html : ' <i class="fa fa-refresh"></i>',
32921             preventDefault: true,
32922             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
32923         });
32924         
32925     },
32926
32927     // private
32928     updateInfo : function(){
32929         if(this.displayEl){
32930             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
32931             var msg = count == 0 ?
32932                 this.emptyMsg :
32933                 String.format(
32934                     this.displayMsg,
32935                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
32936                 );
32937             this.displayEl.update(msg);
32938         }
32939     },
32940
32941     // private
32942     onLoad : function(ds, r, o)
32943     {
32944         this.cursor = o.params && o.params.start ? o.params.start : 0;
32945         
32946         var d = this.getPageData(),
32947             ap = d.activePage,
32948             ps = d.pages;
32949         
32950         
32951         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
32952         this.field.dom.value = ap;
32953         this.first.setDisabled(ap == 1);
32954         this.prev.setDisabled(ap == 1);
32955         this.next.setDisabled(ap == ps);
32956         this.last.setDisabled(ap == ps);
32957         this.loading.enable();
32958         this.updateInfo();
32959     },
32960
32961     // private
32962     getPageData : function(){
32963         var total = this.ds.getTotalCount();
32964         return {
32965             total : total,
32966             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32967             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32968         };
32969     },
32970
32971     // private
32972     onLoadError : function(proxy, o){
32973         this.loading.enable();
32974         if (this.ds.events.loadexception.listeners.length  < 2) {
32975             // nothing has been assigned to loadexception except this...
32976             // so 
32977             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
32978
32979         }
32980     },
32981
32982     // private
32983     onPagingKeydown : function(e){
32984         var k = e.getKey();
32985         var d = this.getPageData();
32986         if(k == e.RETURN){
32987             var v = this.field.dom.value, pageNum;
32988             if(!v || isNaN(pageNum = parseInt(v, 10))){
32989                 this.field.dom.value = d.activePage;
32990                 return;
32991             }
32992             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32993             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32994             e.stopEvent();
32995         }
32996         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))
32997         {
32998           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32999           this.field.dom.value = pageNum;
33000           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
33001           e.stopEvent();
33002         }
33003         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
33004         {
33005           var v = this.field.dom.value, pageNum; 
33006           var increment = (e.shiftKey) ? 10 : 1;
33007           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33008                 increment *= -1;
33009           }
33010           if(!v || isNaN(pageNum = parseInt(v, 10))) {
33011             this.field.dom.value = d.activePage;
33012             return;
33013           }
33014           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33015           {
33016             this.field.dom.value = parseInt(v, 10) + increment;
33017             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33018             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33019           }
33020           e.stopEvent();
33021         }
33022     },
33023
33024     // private
33025     beforeLoad : function(){
33026         if(this.loading){
33027             this.loading.disable();
33028         }
33029     },
33030
33031     // private
33032     onClick : function(which){
33033         
33034         var ds = this.ds;
33035         if (!ds) {
33036             return;
33037         }
33038         
33039         switch(which){
33040             case "first":
33041                 ds.load({params:{start: 0, limit: this.pageSize}});
33042             break;
33043             case "prev":
33044                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33045             break;
33046             case "next":
33047                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33048             break;
33049             case "last":
33050                 var total = ds.getTotalCount();
33051                 var extra = total % this.pageSize;
33052                 var lastStart = extra ? (total - extra) : total-this.pageSize;
33053                 ds.load({params:{start: lastStart, limit: this.pageSize}});
33054             break;
33055             case "refresh":
33056                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33057             break;
33058         }
33059     },
33060
33061     /**
33062      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33063      * @param {Roo.data.Store} store The data store to unbind
33064      */
33065     unbind : function(ds){
33066         ds.un("beforeload", this.beforeLoad, this);
33067         ds.un("load", this.onLoad, this);
33068         ds.un("loadexception", this.onLoadError, this);
33069         ds.un("remove", this.updateInfo, this);
33070         ds.un("add", this.updateInfo, this);
33071         this.ds = undefined;
33072     },
33073
33074     /**
33075      * Binds the paging toolbar to the specified {@link Roo.data.Store}
33076      * @param {Roo.data.Store} store The data store to bind
33077      */
33078     bind : function(ds){
33079         ds.on("beforeload", this.beforeLoad, this);
33080         ds.on("load", this.onLoad, this);
33081         ds.on("loadexception", this.onLoadError, this);
33082         ds.on("remove", this.updateInfo, this);
33083         ds.on("add", this.updateInfo, this);
33084         this.ds = ds;
33085     }
33086 });/*
33087  * - LGPL
33088  *
33089  * element
33090  * 
33091  */
33092
33093 /**
33094  * @class Roo.bootstrap.MessageBar
33095  * @extends Roo.bootstrap.Component
33096  * Bootstrap MessageBar class
33097  * @cfg {String} html contents of the MessageBar
33098  * @cfg {String} weight (info | success | warning | danger) default info
33099  * @cfg {String} beforeClass insert the bar before the given class
33100  * @cfg {Boolean} closable (true | false) default false
33101  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33102  * 
33103  * @constructor
33104  * Create a new Element
33105  * @param {Object} config The config object
33106  */
33107
33108 Roo.bootstrap.MessageBar = function(config){
33109     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33110 };
33111
33112 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
33113     
33114     html: '',
33115     weight: 'info',
33116     closable: false,
33117     fixed: false,
33118     beforeClass: 'bootstrap-sticky-wrap',
33119     
33120     getAutoCreate : function(){
33121         
33122         var cfg = {
33123             tag: 'div',
33124             cls: 'alert alert-dismissable alert-' + this.weight,
33125             cn: [
33126                 {
33127                     tag: 'span',
33128                     cls: 'message',
33129                     html: this.html || ''
33130                 }
33131             ]
33132         };
33133         
33134         if(this.fixed){
33135             cfg.cls += ' alert-messages-fixed';
33136         }
33137         
33138         if(this.closable){
33139             cfg.cn.push({
33140                 tag: 'button',
33141                 cls: 'close',
33142                 html: 'x'
33143             });
33144         }
33145         
33146         return cfg;
33147     },
33148     
33149     onRender : function(ct, position)
33150     {
33151         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33152         
33153         if(!this.el){
33154             var cfg = Roo.apply({},  this.getAutoCreate());
33155             cfg.id = Roo.id();
33156             
33157             if (this.cls) {
33158                 cfg.cls += ' ' + this.cls;
33159             }
33160             if (this.style) {
33161                 cfg.style = this.style;
33162             }
33163             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33164             
33165             this.el.setVisibilityMode(Roo.Element.DISPLAY);
33166         }
33167         
33168         this.el.select('>button.close').on('click', this.hide, this);
33169         
33170     },
33171     
33172     show : function()
33173     {
33174         if (!this.rendered) {
33175             this.render();
33176         }
33177         
33178         this.el.show();
33179         
33180         this.fireEvent('show', this);
33181         
33182     },
33183     
33184     hide : function()
33185     {
33186         if (!this.rendered) {
33187             this.render();
33188         }
33189         
33190         this.el.hide();
33191         
33192         this.fireEvent('hide', this);
33193     },
33194     
33195     update : function()
33196     {
33197 //        var e = this.el.dom.firstChild;
33198 //        
33199 //        if(this.closable){
33200 //            e = e.nextSibling;
33201 //        }
33202 //        
33203 //        e.data = this.html || '';
33204
33205         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
33206     }
33207    
33208 });
33209
33210  
33211
33212      /*
33213  * - LGPL
33214  *
33215  * Graph
33216  * 
33217  */
33218
33219
33220 /**
33221  * @class Roo.bootstrap.Graph
33222  * @extends Roo.bootstrap.Component
33223  * Bootstrap Graph class
33224 > Prameters
33225  -sm {number} sm 4
33226  -md {number} md 5
33227  @cfg {String} graphtype  bar | vbar | pie
33228  @cfg {number} g_x coodinator | centre x (pie)
33229  @cfg {number} g_y coodinator | centre y (pie)
33230  @cfg {number} g_r radius (pie)
33231  @cfg {number} g_height height of the chart (respected by all elements in the set)
33232  @cfg {number} g_width width of the chart (respected by all elements in the set)
33233  @cfg {Object} title The title of the chart
33234     
33235  -{Array}  values
33236  -opts (object) options for the chart 
33237      o {
33238      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
33239      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
33240      o vgutter (number)
33241      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.
33242      o stacked (boolean) whether or not to tread values as in a stacked bar chart
33243      o to
33244      o stretch (boolean)
33245      o }
33246  -opts (object) options for the pie
33247      o{
33248      o cut
33249      o startAngle (number)
33250      o endAngle (number)
33251      } 
33252  *
33253  * @constructor
33254  * Create a new Input
33255  * @param {Object} config The config object
33256  */
33257
33258 Roo.bootstrap.Graph = function(config){
33259     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
33260     
33261     this.addEvents({
33262         // img events
33263         /**
33264          * @event click
33265          * The img click event for the img.
33266          * @param {Roo.EventObject} e
33267          */
33268         "click" : true
33269     });
33270 };
33271
33272 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
33273     
33274     sm: 4,
33275     md: 5,
33276     graphtype: 'bar',
33277     g_height: 250,
33278     g_width: 400,
33279     g_x: 50,
33280     g_y: 50,
33281     g_r: 30,
33282     opts:{
33283         //g_colors: this.colors,
33284         g_type: 'soft',
33285         g_gutter: '20%'
33286
33287     },
33288     title : false,
33289
33290     getAutoCreate : function(){
33291         
33292         var cfg = {
33293             tag: 'div',
33294             html : null
33295         };
33296         
33297         
33298         return  cfg;
33299     },
33300
33301     onRender : function(ct,position){
33302         
33303         
33304         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
33305         
33306         if (typeof(Raphael) == 'undefined') {
33307             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
33308             return;
33309         }
33310         
33311         this.raphael = Raphael(this.el.dom);
33312         
33313                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33314                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33315                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33316                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
33317                 /*
33318                 r.text(160, 10, "Single Series Chart").attr(txtattr);
33319                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
33320                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
33321                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
33322                 
33323                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
33324                 r.barchart(330, 10, 300, 220, data1);
33325                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
33326                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
33327                 */
33328                 
33329                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33330                 // r.barchart(30, 30, 560, 250,  xdata, {
33331                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
33332                 //     axis : "0 0 1 1",
33333                 //     axisxlabels :  xdata
33334                 //     //yvalues : cols,
33335                    
33336                 // });
33337 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33338 //        
33339 //        this.load(null,xdata,{
33340 //                axis : "0 0 1 1",
33341 //                axisxlabels :  xdata
33342 //                });
33343
33344     },
33345
33346     load : function(graphtype,xdata,opts)
33347     {
33348         this.raphael.clear();
33349         if(!graphtype) {
33350             graphtype = this.graphtype;
33351         }
33352         if(!opts){
33353             opts = this.opts;
33354         }
33355         var r = this.raphael,
33356             fin = function () {
33357                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
33358             },
33359             fout = function () {
33360                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
33361             },
33362             pfin = function() {
33363                 this.sector.stop();
33364                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
33365
33366                 if (this.label) {
33367                     this.label[0].stop();
33368                     this.label[0].attr({ r: 7.5 });
33369                     this.label[1].attr({ "font-weight": 800 });
33370                 }
33371             },
33372             pfout = function() {
33373                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
33374
33375                 if (this.label) {
33376                     this.label[0].animate({ r: 5 }, 500, "bounce");
33377                     this.label[1].attr({ "font-weight": 400 });
33378                 }
33379             };
33380
33381         switch(graphtype){
33382             case 'bar':
33383                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33384                 break;
33385             case 'hbar':
33386                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33387                 break;
33388             case 'pie':
33389 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
33390 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
33391 //            
33392                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
33393                 
33394                 break;
33395
33396         }
33397         
33398         if(this.title){
33399             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
33400         }
33401         
33402     },
33403     
33404     setTitle: function(o)
33405     {
33406         this.title = o;
33407     },
33408     
33409     initEvents: function() {
33410         
33411         if(!this.href){
33412             this.el.on('click', this.onClick, this);
33413         }
33414     },
33415     
33416     onClick : function(e)
33417     {
33418         Roo.log('img onclick');
33419         this.fireEvent('click', this, e);
33420     }
33421    
33422 });
33423
33424  
33425 Roo.bootstrap.dash = {};/*
33426  * - LGPL
33427  *
33428  * numberBox
33429  * 
33430  */
33431 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33432
33433 /**
33434  * @class Roo.bootstrap.dash.NumberBox
33435  * @extends Roo.bootstrap.Component
33436  * Bootstrap NumberBox class
33437  * @cfg {String} headline Box headline
33438  * @cfg {String} content Box content
33439  * @cfg {String} icon Box icon
33440  * @cfg {String} footer Footer text
33441  * @cfg {String} fhref Footer href
33442  * 
33443  * @constructor
33444  * Create a new NumberBox
33445  * @param {Object} config The config object
33446  */
33447
33448
33449 Roo.bootstrap.dash.NumberBox = function(config){
33450     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
33451     
33452 };
33453
33454 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
33455     
33456     headline : '',
33457     content : '',
33458     icon : '',
33459     footer : '',
33460     fhref : '',
33461     ficon : '',
33462     
33463     getAutoCreate : function(){
33464         
33465         var cfg = {
33466             tag : 'div',
33467             cls : 'small-box ',
33468             cn : [
33469                 {
33470                     tag : 'div',
33471                     cls : 'inner',
33472                     cn :[
33473                         {
33474                             tag : 'h3',
33475                             cls : 'roo-headline',
33476                             html : this.headline
33477                         },
33478                         {
33479                             tag : 'p',
33480                             cls : 'roo-content',
33481                             html : this.content
33482                         }
33483                     ]
33484                 }
33485             ]
33486         };
33487         
33488         if(this.icon){
33489             cfg.cn.push({
33490                 tag : 'div',
33491                 cls : 'icon',
33492                 cn :[
33493                     {
33494                         tag : 'i',
33495                         cls : 'ion ' + this.icon
33496                     }
33497                 ]
33498             });
33499         }
33500         
33501         if(this.footer){
33502             var footer = {
33503                 tag : 'a',
33504                 cls : 'small-box-footer',
33505                 href : this.fhref || '#',
33506                 html : this.footer
33507             };
33508             
33509             cfg.cn.push(footer);
33510             
33511         }
33512         
33513         return  cfg;
33514     },
33515
33516     onRender : function(ct,position){
33517         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
33518
33519
33520        
33521                 
33522     },
33523
33524     setHeadline: function (value)
33525     {
33526         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
33527     },
33528     
33529     setFooter: function (value, href)
33530     {
33531         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
33532         
33533         if(href){
33534             this.el.select('a.small-box-footer',true).first().attr('href', href);
33535         }
33536         
33537     },
33538
33539     setContent: function (value)
33540     {
33541         this.el.select('.roo-content',true).first().dom.innerHTML = value;
33542     },
33543
33544     initEvents: function() 
33545     {   
33546         
33547     }
33548     
33549 });
33550
33551  
33552 /*
33553  * - LGPL
33554  *
33555  * TabBox
33556  * 
33557  */
33558 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33559
33560 /**
33561  * @class Roo.bootstrap.dash.TabBox
33562  * @extends Roo.bootstrap.Component
33563  * @children Roo.bootstrap.dash.TabPane
33564  * Bootstrap TabBox class
33565  * @cfg {String} title Title of the TabBox
33566  * @cfg {String} icon Icon of the TabBox
33567  * @cfg {Boolean} showtabs (true|false) show the tabs default true
33568  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
33569  * 
33570  * @constructor
33571  * Create a new TabBox
33572  * @param {Object} config The config object
33573  */
33574
33575
33576 Roo.bootstrap.dash.TabBox = function(config){
33577     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
33578     this.addEvents({
33579         // raw events
33580         /**
33581          * @event addpane
33582          * When a pane is added
33583          * @param {Roo.bootstrap.dash.TabPane} pane
33584          */
33585         "addpane" : true,
33586         /**
33587          * @event activatepane
33588          * When a pane is activated
33589          * @param {Roo.bootstrap.dash.TabPane} pane
33590          */
33591         "activatepane" : true
33592         
33593          
33594     });
33595     
33596     this.panes = [];
33597 };
33598
33599 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
33600
33601     title : '',
33602     icon : false,
33603     showtabs : true,
33604     tabScrollable : false,
33605     
33606     getChildContainer : function()
33607     {
33608         return this.el.select('.tab-content', true).first();
33609     },
33610     
33611     getAutoCreate : function(){
33612         
33613         var header = {
33614             tag: 'li',
33615             cls: 'pull-left header',
33616             html: this.title,
33617             cn : []
33618         };
33619         
33620         if(this.icon){
33621             header.cn.push({
33622                 tag: 'i',
33623                 cls: 'fa ' + this.icon
33624             });
33625         }
33626         
33627         var h = {
33628             tag: 'ul',
33629             cls: 'nav nav-tabs pull-right',
33630             cn: [
33631                 header
33632             ]
33633         };
33634         
33635         if(this.tabScrollable){
33636             h = {
33637                 tag: 'div',
33638                 cls: 'tab-header',
33639                 cn: [
33640                     {
33641                         tag: 'ul',
33642                         cls: 'nav nav-tabs pull-right',
33643                         cn: [
33644                             header
33645                         ]
33646                     }
33647                 ]
33648             };
33649         }
33650         
33651         var cfg = {
33652             tag: 'div',
33653             cls: 'nav-tabs-custom',
33654             cn: [
33655                 h,
33656                 {
33657                     tag: 'div',
33658                     cls: 'tab-content no-padding',
33659                     cn: []
33660                 }
33661             ]
33662         };
33663
33664         return  cfg;
33665     },
33666     initEvents : function()
33667     {
33668         //Roo.log('add add pane handler');
33669         this.on('addpane', this.onAddPane, this);
33670     },
33671      /**
33672      * Updates the box title
33673      * @param {String} html to set the title to.
33674      */
33675     setTitle : function(value)
33676     {
33677         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
33678     },
33679     onAddPane : function(pane)
33680     {
33681         this.panes.push(pane);
33682         //Roo.log('addpane');
33683         //Roo.log(pane);
33684         // tabs are rendere left to right..
33685         if(!this.showtabs){
33686             return;
33687         }
33688         
33689         var ctr = this.el.select('.nav-tabs', true).first();
33690          
33691          
33692         var existing = ctr.select('.nav-tab',true);
33693         var qty = existing.getCount();;
33694         
33695         
33696         var tab = ctr.createChild({
33697             tag : 'li',
33698             cls : 'nav-tab' + (qty ? '' : ' active'),
33699             cn : [
33700                 {
33701                     tag : 'a',
33702                     href:'#',
33703                     html : pane.title
33704                 }
33705             ]
33706         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
33707         pane.tab = tab;
33708         
33709         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
33710         if (!qty) {
33711             pane.el.addClass('active');
33712         }
33713         
33714                 
33715     },
33716     onTabClick : function(ev,un,ob,pane)
33717     {
33718         //Roo.log('tab - prev default');
33719         ev.preventDefault();
33720         
33721         
33722         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
33723         pane.tab.addClass('active');
33724         //Roo.log(pane.title);
33725         this.getChildContainer().select('.tab-pane',true).removeClass('active');
33726         // technically we should have a deactivate event.. but maybe add later.
33727         // and it should not de-activate the selected tab...
33728         this.fireEvent('activatepane', pane);
33729         pane.el.addClass('active');
33730         pane.fireEvent('activate');
33731         
33732         
33733     },
33734     
33735     getActivePane : function()
33736     {
33737         var r = false;
33738         Roo.each(this.panes, function(p) {
33739             if(p.el.hasClass('active')){
33740                 r = p;
33741                 return false;
33742             }
33743             
33744             return;
33745         });
33746         
33747         return r;
33748     }
33749     
33750     
33751 });
33752
33753  
33754 /*
33755  * - LGPL
33756  *
33757  * Tab pane
33758  * 
33759  */
33760 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33761 /**
33762  * @class Roo.bootstrap.TabPane
33763  * @extends Roo.bootstrap.Component
33764  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
33765  * Bootstrap TabPane class
33766  * @cfg {Boolean} active (false | true) Default false
33767  * @cfg {String} title title of panel
33768
33769  * 
33770  * @constructor
33771  * Create a new TabPane
33772  * @param {Object} config The config object
33773  */
33774
33775 Roo.bootstrap.dash.TabPane = function(config){
33776     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
33777     
33778     this.addEvents({
33779         // raw events
33780         /**
33781          * @event activate
33782          * When a pane is activated
33783          * @param {Roo.bootstrap.dash.TabPane} pane
33784          */
33785         "activate" : true
33786          
33787     });
33788 };
33789
33790 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
33791     
33792     active : false,
33793     title : '',
33794     
33795     // the tabBox that this is attached to.
33796     tab : false,
33797      
33798     getAutoCreate : function() 
33799     {
33800         var cfg = {
33801             tag: 'div',
33802             cls: 'tab-pane'
33803         };
33804         
33805         if(this.active){
33806             cfg.cls += ' active';
33807         }
33808         
33809         return cfg;
33810     },
33811     initEvents  : function()
33812     {
33813         //Roo.log('trigger add pane handler');
33814         this.parent().fireEvent('addpane', this)
33815     },
33816     
33817      /**
33818      * Updates the tab title 
33819      * @param {String} html to set the title to.
33820      */
33821     setTitle: function(str)
33822     {
33823         if (!this.tab) {
33824             return;
33825         }
33826         this.title = str;
33827         this.tab.select('a', true).first().dom.innerHTML = str;
33828         
33829     }
33830     
33831     
33832     
33833 });
33834
33835  
33836
33837
33838  /*
33839  * - LGPL
33840  *
33841  * Tooltip
33842  * 
33843  */
33844
33845 /**
33846  * @class Roo.bootstrap.Tooltip
33847  * Bootstrap Tooltip class
33848  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
33849  * to determine which dom element triggers the tooltip.
33850  * 
33851  * It needs to add support for additional attributes like tooltip-position
33852  * 
33853  * @constructor
33854  * Create a new Toolti
33855  * @param {Object} config The config object
33856  */
33857
33858 Roo.bootstrap.Tooltip = function(config){
33859     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
33860     
33861     this.alignment = Roo.bootstrap.Tooltip.alignment;
33862     
33863     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
33864         this.alignment = config.alignment;
33865     }
33866     
33867 };
33868
33869 Roo.apply(Roo.bootstrap.Tooltip, {
33870     /**
33871      * @function init initialize tooltip monitoring.
33872      * @static
33873      */
33874     currentEl : false,
33875     currentTip : false,
33876     currentRegion : false,
33877     
33878     //  init : delay?
33879     
33880     init : function()
33881     {
33882         Roo.get(document).on('mouseover', this.enter ,this);
33883         Roo.get(document).on('mouseout', this.leave, this);
33884          
33885         
33886         this.currentTip = new Roo.bootstrap.Tooltip();
33887     },
33888     
33889     enter : function(ev)
33890     {
33891         var dom = ev.getTarget();
33892         
33893         //Roo.log(['enter',dom]);
33894         var el = Roo.fly(dom);
33895         if (this.currentEl) {
33896             //Roo.log(dom);
33897             //Roo.log(this.currentEl);
33898             //Roo.log(this.currentEl.contains(dom));
33899             if (this.currentEl == el) {
33900                 return;
33901             }
33902             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
33903                 return;
33904             }
33905
33906         }
33907         
33908         if (this.currentTip.el) {
33909             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
33910         }    
33911         //Roo.log(ev);
33912         
33913         if(!el || el.dom == document){
33914             return;
33915         }
33916         
33917         var bindEl = el; 
33918         var pel = false;
33919         if (!el.attr('tooltip')) {
33920             pel = el.findParent("[tooltip]");
33921             if (pel) {
33922                 bindEl = Roo.get(pel);
33923             }
33924         }
33925         
33926        
33927         
33928         // you can not look for children, as if el is the body.. then everythign is the child..
33929         if (!pel && !el.attr('tooltip')) { //
33930             if (!el.select("[tooltip]").elements.length) {
33931                 return;
33932             }
33933             // is the mouse over this child...?
33934             bindEl = el.select("[tooltip]").first();
33935             var xy = ev.getXY();
33936             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
33937                 //Roo.log("not in region.");
33938                 return;
33939             }
33940             //Roo.log("child element over..");
33941             
33942         }
33943         this.currentEl = el;
33944         this.currentTip.bind(bindEl);
33945         this.currentRegion = Roo.lib.Region.getRegion(dom);
33946         this.currentTip.enter();
33947         
33948     },
33949     leave : function(ev)
33950     {
33951         var dom = ev.getTarget();
33952         //Roo.log(['leave',dom]);
33953         if (!this.currentEl) {
33954             return;
33955         }
33956         
33957         
33958         if (dom != this.currentEl.dom) {
33959             return;
33960         }
33961         var xy = ev.getXY();
33962         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
33963             return;
33964         }
33965         // only activate leave if mouse cursor is outside... bounding box..
33966         
33967         
33968         
33969         
33970         if (this.currentTip) {
33971             this.currentTip.leave();
33972         }
33973         //Roo.log('clear currentEl');
33974         this.currentEl = false;
33975         
33976         
33977     },
33978     alignment : {
33979         'left' : ['r-l', [-2,0], 'right'],
33980         'right' : ['l-r', [2,0], 'left'],
33981         'bottom' : ['t-b', [0,2], 'top'],
33982         'top' : [ 'b-t', [0,-2], 'bottom']
33983     }
33984     
33985 });
33986
33987
33988 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
33989     
33990     
33991     bindEl : false,
33992     
33993     delay : null, // can be { show : 300 , hide: 500}
33994     
33995     timeout : null,
33996     
33997     hoverState : null, //???
33998     
33999     placement : 'bottom', 
34000     
34001     alignment : false,
34002     
34003     getAutoCreate : function(){
34004     
34005         var cfg = {
34006            cls : 'tooltip',   
34007            role : 'tooltip',
34008            cn : [
34009                 {
34010                     cls : 'tooltip-arrow arrow'
34011                 },
34012                 {
34013                     cls : 'tooltip-inner'
34014                 }
34015            ]
34016         };
34017         
34018         return cfg;
34019     },
34020     bind : function(el)
34021     {
34022         this.bindEl = el;
34023     },
34024     
34025     initEvents : function()
34026     {
34027         this.arrowEl = this.el.select('.arrow', true).first();
34028         this.innerEl = this.el.select('.tooltip-inner', true).first();
34029     },
34030     
34031     enter : function () {
34032        
34033         if (this.timeout != null) {
34034             clearTimeout(this.timeout);
34035         }
34036         
34037         this.hoverState = 'in';
34038          //Roo.log("enter - show");
34039         if (!this.delay || !this.delay.show) {
34040             this.show();
34041             return;
34042         }
34043         var _t = this;
34044         this.timeout = setTimeout(function () {
34045             if (_t.hoverState == 'in') {
34046                 _t.show();
34047             }
34048         }, this.delay.show);
34049     },
34050     leave : function()
34051     {
34052         clearTimeout(this.timeout);
34053     
34054         this.hoverState = 'out';
34055          if (!this.delay || !this.delay.hide) {
34056             this.hide();
34057             return;
34058         }
34059        
34060         var _t = this;
34061         this.timeout = setTimeout(function () {
34062             //Roo.log("leave - timeout");
34063             
34064             if (_t.hoverState == 'out') {
34065                 _t.hide();
34066                 Roo.bootstrap.Tooltip.currentEl = false;
34067             }
34068         }, delay);
34069     },
34070     
34071     show : function (msg)
34072     {
34073         if (!this.el) {
34074             this.render(document.body);
34075         }
34076         // set content.
34077         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34078         
34079         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34080         
34081         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34082         
34083         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34084                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34085
34086         if(this.bindEl.attr('tooltip-class')) {
34087             this.el.addClass(this.bindEl.attr('tooltip-class'));
34088         }
34089         
34090         var placement = typeof this.placement == 'function' ?
34091             this.placement.call(this, this.el, on_el) :
34092             this.placement;
34093         
34094         if(this.bindEl.attr('tooltip-placement')) {
34095             placement = this.bindEl.attr('tooltip-placement');
34096         }
34097             
34098         var autoToken = /\s?auto?\s?/i;
34099         var autoPlace = autoToken.test(placement);
34100         if (autoPlace) {
34101             placement = placement.replace(autoToken, '') || 'top';
34102         }
34103         
34104         //this.el.detach()
34105         //this.el.setXY([0,0]);
34106         this.el.show();
34107         //this.el.dom.style.display='block';
34108         
34109         //this.el.appendTo(on_el);
34110         
34111         var p = this.getPosition();
34112         var box = this.el.getBox();
34113         
34114         if (autoPlace) {
34115             // fixme..
34116         }
34117         
34118         var align = this.alignment[placement];
34119         
34120         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34121         
34122         if(placement == 'top' || placement == 'bottom'){
34123             if(xy[0] < 0){
34124                 placement = 'right';
34125             }
34126             
34127             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34128                 placement = 'left';
34129             }
34130             
34131             var scroll = Roo.select('body', true).first().getScroll();
34132             
34133             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34134                 placement = 'top';
34135             }
34136             
34137             align = this.alignment[placement];
34138             
34139             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34140             
34141         }
34142         
34143         var elems = document.getElementsByTagName('div');
34144         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34145         for (var i = 0; i < elems.length; i++) {
34146           var zindex = Number.parseInt(
34147                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34148                 10
34149           );
34150           if (zindex > highest) {
34151             highest = zindex;
34152           }
34153         }
34154         
34155         
34156         
34157         this.el.dom.style.zIndex = highest;
34158         
34159         this.el.alignTo(this.bindEl, align[0],align[1]);
34160         //var arrow = this.el.select('.arrow',true).first();
34161         //arrow.set(align[2], 
34162         
34163         this.el.addClass(placement);
34164         this.el.addClass("bs-tooltip-"+ placement);
34165         
34166         this.el.addClass('in fade show');
34167         
34168         this.hoverState = null;
34169         
34170         if (this.el.hasClass('fade')) {
34171             // fade it?
34172         }
34173         
34174         
34175         
34176         
34177         
34178     },
34179     hide : function()
34180     {
34181          
34182         if (!this.el) {
34183             return;
34184         }
34185         //this.el.setXY([0,0]);
34186         if(this.bindEl.attr('tooltip-class')) {
34187             this.el.removeClass(this.bindEl.attr('tooltip-class'));
34188         }
34189         this.el.removeClass(['show', 'in']);
34190         //this.el.hide();
34191         
34192     }
34193     
34194 });
34195  
34196
34197  /*
34198  * - LGPL
34199  *
34200  * Location Picker
34201  * 
34202  */
34203
34204 /**
34205  * @class Roo.bootstrap.LocationPicker
34206  * @extends Roo.bootstrap.Component
34207  * Bootstrap LocationPicker class
34208  * @cfg {Number} latitude Position when init default 0
34209  * @cfg {Number} longitude Position when init default 0
34210  * @cfg {Number} zoom default 15
34211  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
34212  * @cfg {Boolean} mapTypeControl default false
34213  * @cfg {Boolean} disableDoubleClickZoom default false
34214  * @cfg {Boolean} scrollwheel default true
34215  * @cfg {Boolean} streetViewControl default false
34216  * @cfg {Number} radius default 0
34217  * @cfg {String} locationName
34218  * @cfg {Boolean} draggable default true
34219  * @cfg {Boolean} enableAutocomplete default false
34220  * @cfg {Boolean} enableReverseGeocode default true
34221  * @cfg {String} markerTitle
34222  * 
34223  * @constructor
34224  * Create a new LocationPicker
34225  * @param {Object} config The config object
34226  */
34227
34228
34229 Roo.bootstrap.LocationPicker = function(config){
34230     
34231     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
34232     
34233     this.addEvents({
34234         /**
34235          * @event initial
34236          * Fires when the picker initialized.
34237          * @param {Roo.bootstrap.LocationPicker} this
34238          * @param {Google Location} location
34239          */
34240         initial : true,
34241         /**
34242          * @event positionchanged
34243          * Fires when the picker position changed.
34244          * @param {Roo.bootstrap.LocationPicker} this
34245          * @param {Google Location} location
34246          */
34247         positionchanged : true,
34248         /**
34249          * @event resize
34250          * Fires when the map resize.
34251          * @param {Roo.bootstrap.LocationPicker} this
34252          */
34253         resize : true,
34254         /**
34255          * @event show
34256          * Fires when the map show.
34257          * @param {Roo.bootstrap.LocationPicker} this
34258          */
34259         show : true,
34260         /**
34261          * @event hide
34262          * Fires when the map hide.
34263          * @param {Roo.bootstrap.LocationPicker} this
34264          */
34265         hide : true,
34266         /**
34267          * @event mapClick
34268          * Fires when click the map.
34269          * @param {Roo.bootstrap.LocationPicker} this
34270          * @param {Map event} e
34271          */
34272         mapClick : true,
34273         /**
34274          * @event mapRightClick
34275          * Fires when right click the map.
34276          * @param {Roo.bootstrap.LocationPicker} this
34277          * @param {Map event} e
34278          */
34279         mapRightClick : true,
34280         /**
34281          * @event markerClick
34282          * Fires when click the marker.
34283          * @param {Roo.bootstrap.LocationPicker} this
34284          * @param {Map event} e
34285          */
34286         markerClick : true,
34287         /**
34288          * @event markerRightClick
34289          * Fires when right click the marker.
34290          * @param {Roo.bootstrap.LocationPicker} this
34291          * @param {Map event} e
34292          */
34293         markerRightClick : true,
34294         /**
34295          * @event OverlayViewDraw
34296          * Fires when OverlayView Draw
34297          * @param {Roo.bootstrap.LocationPicker} this
34298          */
34299         OverlayViewDraw : true,
34300         /**
34301          * @event OverlayViewOnAdd
34302          * Fires when OverlayView Draw
34303          * @param {Roo.bootstrap.LocationPicker} this
34304          */
34305         OverlayViewOnAdd : true,
34306         /**
34307          * @event OverlayViewOnRemove
34308          * Fires when OverlayView Draw
34309          * @param {Roo.bootstrap.LocationPicker} this
34310          */
34311         OverlayViewOnRemove : true,
34312         /**
34313          * @event OverlayViewShow
34314          * Fires when OverlayView Draw
34315          * @param {Roo.bootstrap.LocationPicker} this
34316          * @param {Pixel} cpx
34317          */
34318         OverlayViewShow : true,
34319         /**
34320          * @event OverlayViewHide
34321          * Fires when OverlayView Draw
34322          * @param {Roo.bootstrap.LocationPicker} this
34323          */
34324         OverlayViewHide : true,
34325         /**
34326          * @event loadexception
34327          * Fires when load google lib failed.
34328          * @param {Roo.bootstrap.LocationPicker} this
34329          */
34330         loadexception : true
34331     });
34332         
34333 };
34334
34335 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
34336     
34337     gMapContext: false,
34338     
34339     latitude: 0,
34340     longitude: 0,
34341     zoom: 15,
34342     mapTypeId: false,
34343     mapTypeControl: false,
34344     disableDoubleClickZoom: false,
34345     scrollwheel: true,
34346     streetViewControl: false,
34347     radius: 0,
34348     locationName: '',
34349     draggable: true,
34350     enableAutocomplete: false,
34351     enableReverseGeocode: true,
34352     markerTitle: '',
34353     
34354     getAutoCreate: function()
34355     {
34356
34357         var cfg = {
34358             tag: 'div',
34359             cls: 'roo-location-picker'
34360         };
34361         
34362         return cfg
34363     },
34364     
34365     initEvents: function(ct, position)
34366     {       
34367         if(!this.el.getWidth() || this.isApplied()){
34368             return;
34369         }
34370         
34371         this.el.setVisibilityMode(Roo.Element.DISPLAY);
34372         
34373         this.initial();
34374     },
34375     
34376     initial: function()
34377     {
34378         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
34379             this.fireEvent('loadexception', this);
34380             return;
34381         }
34382         
34383         if(!this.mapTypeId){
34384             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
34385         }
34386         
34387         this.gMapContext = this.GMapContext();
34388         
34389         this.initOverlayView();
34390         
34391         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
34392         
34393         var _this = this;
34394                 
34395         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
34396             _this.setPosition(_this.gMapContext.marker.position);
34397         });
34398         
34399         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
34400             _this.fireEvent('mapClick', this, event);
34401             
34402         });
34403
34404         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
34405             _this.fireEvent('mapRightClick', this, event);
34406             
34407         });
34408         
34409         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
34410             _this.fireEvent('markerClick', this, event);
34411             
34412         });
34413
34414         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
34415             _this.fireEvent('markerRightClick', this, event);
34416             
34417         });
34418         
34419         this.setPosition(this.gMapContext.location);
34420         
34421         this.fireEvent('initial', this, this.gMapContext.location);
34422     },
34423     
34424     initOverlayView: function()
34425     {
34426         var _this = this;
34427         
34428         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
34429             
34430             draw: function()
34431             {
34432                 _this.fireEvent('OverlayViewDraw', _this);
34433             },
34434             
34435             onAdd: function()
34436             {
34437                 _this.fireEvent('OverlayViewOnAdd', _this);
34438             },
34439             
34440             onRemove: function()
34441             {
34442                 _this.fireEvent('OverlayViewOnRemove', _this);
34443             },
34444             
34445             show: function(cpx)
34446             {
34447                 _this.fireEvent('OverlayViewShow', _this, cpx);
34448             },
34449             
34450             hide: function()
34451             {
34452                 _this.fireEvent('OverlayViewHide', _this);
34453             }
34454             
34455         });
34456     },
34457     
34458     fromLatLngToContainerPixel: function(event)
34459     {
34460         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
34461     },
34462     
34463     isApplied: function() 
34464     {
34465         return this.getGmapContext() == false ? false : true;
34466     },
34467     
34468     getGmapContext: function() 
34469     {
34470         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
34471     },
34472     
34473     GMapContext: function() 
34474     {
34475         var position = new google.maps.LatLng(this.latitude, this.longitude);
34476         
34477         var _map = new google.maps.Map(this.el.dom, {
34478             center: position,
34479             zoom: this.zoom,
34480             mapTypeId: this.mapTypeId,
34481             mapTypeControl: this.mapTypeControl,
34482             disableDoubleClickZoom: this.disableDoubleClickZoom,
34483             scrollwheel: this.scrollwheel,
34484             streetViewControl: this.streetViewControl,
34485             locationName: this.locationName,
34486             draggable: this.draggable,
34487             enableAutocomplete: this.enableAutocomplete,
34488             enableReverseGeocode: this.enableReverseGeocode
34489         });
34490         
34491         var _marker = new google.maps.Marker({
34492             position: position,
34493             map: _map,
34494             title: this.markerTitle,
34495             draggable: this.draggable
34496         });
34497         
34498         return {
34499             map: _map,
34500             marker: _marker,
34501             circle: null,
34502             location: position,
34503             radius: this.radius,
34504             locationName: this.locationName,
34505             addressComponents: {
34506                 formatted_address: null,
34507                 addressLine1: null,
34508                 addressLine2: null,
34509                 streetName: null,
34510                 streetNumber: null,
34511                 city: null,
34512                 district: null,
34513                 state: null,
34514                 stateOrProvince: null
34515             },
34516             settings: this,
34517             domContainer: this.el.dom,
34518             geodecoder: new google.maps.Geocoder()
34519         };
34520     },
34521     
34522     drawCircle: function(center, radius, options) 
34523     {
34524         if (this.gMapContext.circle != null) {
34525             this.gMapContext.circle.setMap(null);
34526         }
34527         if (radius > 0) {
34528             radius *= 1;
34529             options = Roo.apply({}, options, {
34530                 strokeColor: "#0000FF",
34531                 strokeOpacity: .35,
34532                 strokeWeight: 2,
34533                 fillColor: "#0000FF",
34534                 fillOpacity: .2
34535             });
34536             
34537             options.map = this.gMapContext.map;
34538             options.radius = radius;
34539             options.center = center;
34540             this.gMapContext.circle = new google.maps.Circle(options);
34541             return this.gMapContext.circle;
34542         }
34543         
34544         return null;
34545     },
34546     
34547     setPosition: function(location) 
34548     {
34549         this.gMapContext.location = location;
34550         this.gMapContext.marker.setPosition(location);
34551         this.gMapContext.map.panTo(location);
34552         this.drawCircle(location, this.gMapContext.radius, {});
34553         
34554         var _this = this;
34555         
34556         if (this.gMapContext.settings.enableReverseGeocode) {
34557             this.gMapContext.geodecoder.geocode({
34558                 latLng: this.gMapContext.location
34559             }, function(results, status) {
34560                 
34561                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
34562                     _this.gMapContext.locationName = results[0].formatted_address;
34563                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
34564                     
34565                     _this.fireEvent('positionchanged', this, location);
34566                 }
34567             });
34568             
34569             return;
34570         }
34571         
34572         this.fireEvent('positionchanged', this, location);
34573     },
34574     
34575     resize: function()
34576     {
34577         google.maps.event.trigger(this.gMapContext.map, "resize");
34578         
34579         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
34580         
34581         this.fireEvent('resize', this);
34582     },
34583     
34584     setPositionByLatLng: function(latitude, longitude)
34585     {
34586         this.setPosition(new google.maps.LatLng(latitude, longitude));
34587     },
34588     
34589     getCurrentPosition: function() 
34590     {
34591         return {
34592             latitude: this.gMapContext.location.lat(),
34593             longitude: this.gMapContext.location.lng()
34594         };
34595     },
34596     
34597     getAddressName: function() 
34598     {
34599         return this.gMapContext.locationName;
34600     },
34601     
34602     getAddressComponents: function() 
34603     {
34604         return this.gMapContext.addressComponents;
34605     },
34606     
34607     address_component_from_google_geocode: function(address_components) 
34608     {
34609         var result = {};
34610         
34611         for (var i = 0; i < address_components.length; i++) {
34612             var component = address_components[i];
34613             if (component.types.indexOf("postal_code") >= 0) {
34614                 result.postalCode = component.short_name;
34615             } else if (component.types.indexOf("street_number") >= 0) {
34616                 result.streetNumber = component.short_name;
34617             } else if (component.types.indexOf("route") >= 0) {
34618                 result.streetName = component.short_name;
34619             } else if (component.types.indexOf("neighborhood") >= 0) {
34620                 result.city = component.short_name;
34621             } else if (component.types.indexOf("locality") >= 0) {
34622                 result.city = component.short_name;
34623             } else if (component.types.indexOf("sublocality") >= 0) {
34624                 result.district = component.short_name;
34625             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
34626                 result.stateOrProvince = component.short_name;
34627             } else if (component.types.indexOf("country") >= 0) {
34628                 result.country = component.short_name;
34629             }
34630         }
34631         
34632         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
34633         result.addressLine2 = "";
34634         return result;
34635     },
34636     
34637     setZoomLevel: function(zoom)
34638     {
34639         this.gMapContext.map.setZoom(zoom);
34640     },
34641     
34642     show: function()
34643     {
34644         if(!this.el){
34645             return;
34646         }
34647         
34648         this.el.show();
34649         
34650         this.resize();
34651         
34652         this.fireEvent('show', this);
34653     },
34654     
34655     hide: function()
34656     {
34657         if(!this.el){
34658             return;
34659         }
34660         
34661         this.el.hide();
34662         
34663         this.fireEvent('hide', this);
34664     }
34665     
34666 });
34667
34668 Roo.apply(Roo.bootstrap.LocationPicker, {
34669     
34670     OverlayView : function(map, options)
34671     {
34672         options = options || {};
34673         
34674         this.setMap(map);
34675     }
34676     
34677     
34678 });/**
34679  * @class Roo.bootstrap.Alert
34680  * @extends Roo.bootstrap.Component
34681  * Bootstrap Alert class - shows an alert area box
34682  * eg
34683  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
34684   Enter a valid email address
34685 </div>
34686  * @licence LGPL
34687  * @cfg {String} title The title of alert
34688  * @cfg {String} html The content of alert
34689  * @cfg {String} weight (success|info|warning|danger) Weight of the message
34690  * @cfg {String} fa font-awesomeicon
34691  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
34692  * @cfg {Boolean} close true to show a x closer
34693  * 
34694  * 
34695  * @constructor
34696  * Create a new alert
34697  * @param {Object} config The config object
34698  */
34699
34700
34701 Roo.bootstrap.Alert = function(config){
34702     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
34703     
34704 };
34705
34706 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
34707     
34708     title: '',
34709     html: '',
34710     weight: false,
34711     fa: false,
34712     faicon: false, // BC
34713     close : false,
34714     
34715     
34716     getAutoCreate : function()
34717     {
34718         
34719         var cfg = {
34720             tag : 'div',
34721             cls : 'alert',
34722             cn : [
34723                 {
34724                     tag: 'button',
34725                     type :  "button",
34726                     cls: "close",
34727                     html : '×',
34728                     style : this.close ? '' : 'display:none'
34729                 },
34730                 {
34731                     tag : 'i',
34732                     cls : 'roo-alert-icon'
34733                     
34734                 },
34735                 {
34736                     tag : 'b',
34737                     cls : 'roo-alert-title',
34738                     html : this.title
34739                 },
34740                 {
34741                     tag : 'span',
34742                     cls : 'roo-alert-text',
34743                     html : this.html
34744                 }
34745             ]
34746         };
34747         
34748         if(this.faicon){
34749             cfg.cn[0].cls += ' fa ' + this.faicon;
34750         }
34751         if(this.fa){
34752             cfg.cn[0].cls += ' fa ' + this.fa;
34753         }
34754         
34755         if(this.weight){
34756             cfg.cls += ' alert-' + this.weight;
34757         }
34758         
34759         return cfg;
34760     },
34761     
34762     initEvents: function() 
34763     {
34764         this.el.setVisibilityMode(Roo.Element.DISPLAY);
34765         this.titleEl =  this.el.select('.roo-alert-title',true).first();
34766         this.iconEl = this.el.select('.roo-alert-icon',true).first();
34767         this.htmlEl = this.el.select('.roo-alert-text',true).first();
34768         if (this.seconds > 0) {
34769             this.hide.defer(this.seconds, this);
34770         }
34771     },
34772     /**
34773      * Set the Title Message HTML
34774      * @param {String} html
34775      */
34776     setTitle : function(str)
34777     {
34778         this.titleEl.dom.innerHTML = str;
34779     },
34780      
34781      /**
34782      * Set the Body Message HTML
34783      * @param {String} html
34784      */
34785     setHtml : function(str)
34786     {
34787         this.htmlEl.dom.innerHTML = str;
34788     },
34789     /**
34790      * Set the Weight of the alert
34791      * @param {String} (success|info|warning|danger) weight
34792      */
34793     
34794     setWeight : function(weight)
34795     {
34796         if(this.weight){
34797             this.el.removeClass('alert-' + this.weight);
34798         }
34799         
34800         this.weight = weight;
34801         
34802         this.el.addClass('alert-' + this.weight);
34803     },
34804       /**
34805      * Set the Icon of the alert
34806      * @param {String} see fontawsome names (name without the 'fa-' bit)
34807      */
34808     setIcon : function(icon)
34809     {
34810         if(this.faicon){
34811             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
34812         }
34813         
34814         this.faicon = icon;
34815         
34816         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
34817     },
34818     /**
34819      * Hide the Alert
34820      */
34821     hide: function() 
34822     {
34823         this.el.hide();   
34824     },
34825     /**
34826      * Show the Alert
34827      */
34828     show: function() 
34829     {  
34830         this.el.show();   
34831     }
34832     
34833 });
34834
34835  
34836 /*
34837 * Licence: LGPL
34838 */
34839
34840 /**
34841  * @class Roo.bootstrap.UploadCropbox
34842  * @extends Roo.bootstrap.Component
34843  * Bootstrap UploadCropbox class
34844  * @cfg {String} emptyText show when image has been loaded
34845  * @cfg {String} rotateNotify show when image too small to rotate
34846  * @cfg {Number} errorTimeout default 3000
34847  * @cfg {Number} minWidth default 300
34848  * @cfg {Number} minHeight default 300
34849  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
34850  * @cfg {Boolean} isDocument (true|false) default false
34851  * @cfg {String} url action url
34852  * @cfg {String} paramName default 'imageUpload'
34853  * @cfg {String} method default POST
34854  * @cfg {Boolean} loadMask (true|false) default true
34855  * @cfg {Boolean} loadingText default 'Loading...'
34856  * 
34857  * @constructor
34858  * Create a new UploadCropbox
34859  * @param {Object} config The config object
34860  */
34861
34862 Roo.bootstrap.UploadCropbox = function(config){
34863     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
34864     
34865     this.addEvents({
34866         /**
34867          * @event beforeselectfile
34868          * Fire before select file
34869          * @param {Roo.bootstrap.UploadCropbox} this
34870          */
34871         "beforeselectfile" : true,
34872         /**
34873          * @event initial
34874          * Fire after initEvent
34875          * @param {Roo.bootstrap.UploadCropbox} this
34876          */
34877         "initial" : true,
34878         /**
34879          * @event crop
34880          * Fire after initEvent
34881          * @param {Roo.bootstrap.UploadCropbox} this
34882          * @param {String} data
34883          */
34884         "crop" : true,
34885         /**
34886          * @event prepare
34887          * Fire when preparing the file data
34888          * @param {Roo.bootstrap.UploadCropbox} this
34889          * @param {Object} file
34890          */
34891         "prepare" : true,
34892         /**
34893          * @event exception
34894          * Fire when get exception
34895          * @param {Roo.bootstrap.UploadCropbox} this
34896          * @param {XMLHttpRequest} xhr
34897          */
34898         "exception" : true,
34899         /**
34900          * @event beforeloadcanvas
34901          * Fire before load the canvas
34902          * @param {Roo.bootstrap.UploadCropbox} this
34903          * @param {String} src
34904          */
34905         "beforeloadcanvas" : true,
34906         /**
34907          * @event trash
34908          * Fire when trash image
34909          * @param {Roo.bootstrap.UploadCropbox} this
34910          */
34911         "trash" : true,
34912         /**
34913          * @event download
34914          * Fire when download the image
34915          * @param {Roo.bootstrap.UploadCropbox} this
34916          */
34917         "download" : true,
34918         /**
34919          * @event footerbuttonclick
34920          * Fire when footerbuttonclick
34921          * @param {Roo.bootstrap.UploadCropbox} this
34922          * @param {String} type
34923          */
34924         "footerbuttonclick" : true,
34925         /**
34926          * @event resize
34927          * Fire when resize
34928          * @param {Roo.bootstrap.UploadCropbox} this
34929          */
34930         "resize" : true,
34931         /**
34932          * @event rotate
34933          * Fire when rotate the image
34934          * @param {Roo.bootstrap.UploadCropbox} this
34935          * @param {String} pos
34936          */
34937         "rotate" : true,
34938         /**
34939          * @event inspect
34940          * Fire when inspect the file
34941          * @param {Roo.bootstrap.UploadCropbox} this
34942          * @param {Object} file
34943          */
34944         "inspect" : true,
34945         /**
34946          * @event upload
34947          * Fire when xhr upload the file
34948          * @param {Roo.bootstrap.UploadCropbox} this
34949          * @param {Object} data
34950          */
34951         "upload" : true,
34952         /**
34953          * @event arrange
34954          * Fire when arrange the file data
34955          * @param {Roo.bootstrap.UploadCropbox} this
34956          * @param {Object} formData
34957          */
34958         "arrange" : true
34959     });
34960     
34961     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
34962 };
34963
34964 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
34965     
34966     emptyText : 'Click to upload image',
34967     rotateNotify : 'Image is too small to rotate',
34968     errorTimeout : 3000,
34969     scale : 0,
34970     baseScale : 1,
34971     rotate : 0,
34972     dragable : false,
34973     pinching : false,
34974     mouseX : 0,
34975     mouseY : 0,
34976     cropData : false,
34977     minWidth : 300,
34978     minHeight : 300,
34979     file : false,
34980     exif : {},
34981     baseRotate : 1,
34982     cropType : 'image/jpeg',
34983     buttons : false,
34984     canvasLoaded : false,
34985     isDocument : false,
34986     method : 'POST',
34987     paramName : 'imageUpload',
34988     loadMask : true,
34989     loadingText : 'Loading...',
34990     maskEl : false,
34991     
34992     getAutoCreate : function()
34993     {
34994         var cfg = {
34995             tag : 'div',
34996             cls : 'roo-upload-cropbox',
34997             cn : [
34998                 {
34999                     tag : 'input',
35000                     cls : 'roo-upload-cropbox-selector',
35001                     type : 'file'
35002                 },
35003                 {
35004                     tag : 'div',
35005                     cls : 'roo-upload-cropbox-body',
35006                     style : 'cursor:pointer',
35007                     cn : [
35008                         {
35009                             tag : 'div',
35010                             cls : 'roo-upload-cropbox-preview'
35011                         },
35012                         {
35013                             tag : 'div',
35014                             cls : 'roo-upload-cropbox-thumb'
35015                         },
35016                         {
35017                             tag : 'div',
35018                             cls : 'roo-upload-cropbox-empty-notify',
35019                             html : this.emptyText
35020                         },
35021                         {
35022                             tag : 'div',
35023                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35024                             html : this.rotateNotify
35025                         }
35026                     ]
35027                 },
35028                 {
35029                     tag : 'div',
35030                     cls : 'roo-upload-cropbox-footer',
35031                     cn : {
35032                         tag : 'div',
35033                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35034                         cn : []
35035                     }
35036                 }
35037             ]
35038         };
35039         
35040         return cfg;
35041     },
35042     
35043     onRender : function(ct, position)
35044     {
35045         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35046         
35047         if (this.buttons.length) {
35048             
35049             Roo.each(this.buttons, function(bb) {
35050                 
35051                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35052                 
35053                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35054                 
35055             }, this);
35056         }
35057         
35058         if(this.loadMask){
35059             this.maskEl = this.el;
35060         }
35061     },
35062     
35063     initEvents : function()
35064     {
35065         this.urlAPI = (window.createObjectURL && window) || 
35066                                 (window.URL && URL.revokeObjectURL && URL) || 
35067                                 (window.webkitURL && webkitURL);
35068                         
35069         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35070         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35071         
35072         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35073         this.selectorEl.hide();
35074         
35075         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35076         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35077         
35078         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35079         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35080         this.thumbEl.hide();
35081         
35082         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35083         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35084         
35085         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35086         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35087         this.errorEl.hide();
35088         
35089         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35090         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35091         this.footerEl.hide();
35092         
35093         this.setThumbBoxSize();
35094         
35095         this.bind();
35096         
35097         this.resize();
35098         
35099         this.fireEvent('initial', this);
35100     },
35101
35102     bind : function()
35103     {
35104         var _this = this;
35105         
35106         window.addEventListener("resize", function() { _this.resize(); } );
35107         
35108         this.bodyEl.on('click', this.beforeSelectFile, this);
35109         
35110         if(Roo.isTouch){
35111             this.bodyEl.on('touchstart', this.onTouchStart, this);
35112             this.bodyEl.on('touchmove', this.onTouchMove, this);
35113             this.bodyEl.on('touchend', this.onTouchEnd, this);
35114         }
35115         
35116         if(!Roo.isTouch){
35117             this.bodyEl.on('mousedown', this.onMouseDown, this);
35118             this.bodyEl.on('mousemove', this.onMouseMove, this);
35119             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35120             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35121             Roo.get(document).on('mouseup', this.onMouseUp, this);
35122         }
35123         
35124         this.selectorEl.on('change', this.onFileSelected, this);
35125     },
35126     
35127     reset : function()
35128     {    
35129         this.scale = 0;
35130         this.baseScale = 1;
35131         this.rotate = 0;
35132         this.baseRotate = 1;
35133         this.dragable = false;
35134         this.pinching = false;
35135         this.mouseX = 0;
35136         this.mouseY = 0;
35137         this.cropData = false;
35138         this.notifyEl.dom.innerHTML = this.emptyText;
35139         
35140         this.selectorEl.dom.value = '';
35141         
35142     },
35143     
35144     resize : function()
35145     {
35146         if(this.fireEvent('resize', this) != false){
35147             this.setThumbBoxPosition();
35148             this.setCanvasPosition();
35149         }
35150     },
35151     
35152     onFooterButtonClick : function(e, el, o, type)
35153     {
35154         switch (type) {
35155             case 'rotate-left' :
35156                 this.onRotateLeft(e);
35157                 break;
35158             case 'rotate-right' :
35159                 this.onRotateRight(e);
35160                 break;
35161             case 'picture' :
35162                 this.beforeSelectFile(e);
35163                 break;
35164             case 'trash' :
35165                 this.trash(e);
35166                 break;
35167             case 'crop' :
35168                 this.crop(e);
35169                 break;
35170             case 'download' :
35171                 this.download(e);
35172                 break;
35173             default :
35174                 break;
35175         }
35176         
35177         this.fireEvent('footerbuttonclick', this, type);
35178     },
35179     
35180     beforeSelectFile : function(e)
35181     {
35182         e.preventDefault();
35183         
35184         if(this.fireEvent('beforeselectfile', this) != false){
35185             this.selectorEl.dom.click();
35186         }
35187     },
35188     
35189     onFileSelected : function(e)
35190     {
35191         e.preventDefault();
35192         
35193         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35194             return;
35195         }
35196         
35197         var file = this.selectorEl.dom.files[0];
35198         
35199         if(this.fireEvent('inspect', this, file) != false){
35200             this.prepare(file);
35201         }
35202         
35203     },
35204     
35205     trash : function(e)
35206     {
35207         this.fireEvent('trash', this);
35208     },
35209     
35210     download : function(e)
35211     {
35212         this.fireEvent('download', this);
35213     },
35214     
35215     loadCanvas : function(src)
35216     {   
35217         if(this.fireEvent('beforeloadcanvas', this, src) != false){
35218             
35219             this.reset();
35220             
35221             this.imageEl = document.createElement('img');
35222             
35223             var _this = this;
35224             
35225             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
35226             
35227             this.imageEl.src = src;
35228         }
35229     },
35230     
35231     onLoadCanvas : function()
35232     {   
35233         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
35234         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
35235         
35236         this.bodyEl.un('click', this.beforeSelectFile, this);
35237         
35238         this.notifyEl.hide();
35239         this.thumbEl.show();
35240         this.footerEl.show();
35241         
35242         this.baseRotateLevel();
35243         
35244         if(this.isDocument){
35245             this.setThumbBoxSize();
35246         }
35247         
35248         this.setThumbBoxPosition();
35249         
35250         this.baseScaleLevel();
35251         
35252         this.draw();
35253         
35254         this.resize();
35255         
35256         this.canvasLoaded = true;
35257         
35258         if(this.loadMask){
35259             this.maskEl.unmask();
35260         }
35261         
35262     },
35263     
35264     setCanvasPosition : function()
35265     {   
35266         if(!this.canvasEl){
35267             return;
35268         }
35269         
35270         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
35271         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
35272         
35273         this.previewEl.setLeft(pw);
35274         this.previewEl.setTop(ph);
35275         
35276     },
35277     
35278     onMouseDown : function(e)
35279     {   
35280         e.stopEvent();
35281         
35282         this.dragable = true;
35283         this.pinching = false;
35284         
35285         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
35286             this.dragable = false;
35287             return;
35288         }
35289         
35290         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35291         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35292         
35293     },
35294     
35295     onMouseMove : function(e)
35296     {   
35297         e.stopEvent();
35298         
35299         if(!this.canvasLoaded){
35300             return;
35301         }
35302         
35303         if (!this.dragable){
35304             return;
35305         }
35306         
35307         var minX = Math.ceil(this.thumbEl.getLeft(true));
35308         var minY = Math.ceil(this.thumbEl.getTop(true));
35309         
35310         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
35311         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
35312         
35313         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35314         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35315         
35316         x = x - this.mouseX;
35317         y = y - this.mouseY;
35318         
35319         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
35320         var bgY = Math.ceil(y + this.previewEl.getTop(true));
35321         
35322         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
35323         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
35324         
35325         this.previewEl.setLeft(bgX);
35326         this.previewEl.setTop(bgY);
35327         
35328         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35329         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35330     },
35331     
35332     onMouseUp : function(e)
35333     {   
35334         e.stopEvent();
35335         
35336         this.dragable = false;
35337     },
35338     
35339     onMouseWheel : function(e)
35340     {   
35341         e.stopEvent();
35342         
35343         this.startScale = this.scale;
35344         
35345         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
35346         
35347         if(!this.zoomable()){
35348             this.scale = this.startScale;
35349             return;
35350         }
35351         
35352         this.draw();
35353         
35354         return;
35355     },
35356     
35357     zoomable : function()
35358     {
35359         var minScale = this.thumbEl.getWidth() / this.minWidth;
35360         
35361         if(this.minWidth < this.minHeight){
35362             minScale = this.thumbEl.getHeight() / this.minHeight;
35363         }
35364         
35365         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
35366         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
35367         
35368         if(
35369                 this.isDocument &&
35370                 (this.rotate == 0 || this.rotate == 180) && 
35371                 (
35372                     width > this.imageEl.OriginWidth || 
35373                     height > this.imageEl.OriginHeight ||
35374                     (width < this.minWidth && height < this.minHeight)
35375                 )
35376         ){
35377             return false;
35378         }
35379         
35380         if(
35381                 this.isDocument &&
35382                 (this.rotate == 90 || this.rotate == 270) && 
35383                 (
35384                     width > this.imageEl.OriginWidth || 
35385                     height > this.imageEl.OriginHeight ||
35386                     (width < this.minHeight && height < this.minWidth)
35387                 )
35388         ){
35389             return false;
35390         }
35391         
35392         if(
35393                 !this.isDocument &&
35394                 (this.rotate == 0 || this.rotate == 180) && 
35395                 (
35396                     width < this.minWidth || 
35397                     width > this.imageEl.OriginWidth || 
35398                     height < this.minHeight || 
35399                     height > this.imageEl.OriginHeight
35400                 )
35401         ){
35402             return false;
35403         }
35404         
35405         if(
35406                 !this.isDocument &&
35407                 (this.rotate == 90 || this.rotate == 270) && 
35408                 (
35409                     width < this.minHeight || 
35410                     width > this.imageEl.OriginWidth || 
35411                     height < this.minWidth || 
35412                     height > this.imageEl.OriginHeight
35413                 )
35414         ){
35415             return false;
35416         }
35417         
35418         return true;
35419         
35420     },
35421     
35422     onRotateLeft : function(e)
35423     {   
35424         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35425             
35426             var minScale = this.thumbEl.getWidth() / this.minWidth;
35427             
35428             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35429             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35430             
35431             this.startScale = this.scale;
35432             
35433             while (this.getScaleLevel() < minScale){
35434             
35435                 this.scale = this.scale + 1;
35436                 
35437                 if(!this.zoomable()){
35438                     break;
35439                 }
35440                 
35441                 if(
35442                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35443                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35444                 ){
35445                     continue;
35446                 }
35447                 
35448                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35449
35450                 this.draw();
35451                 
35452                 return;
35453             }
35454             
35455             this.scale = this.startScale;
35456             
35457             this.onRotateFail();
35458             
35459             return false;
35460         }
35461         
35462         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35463
35464         if(this.isDocument){
35465             this.setThumbBoxSize();
35466             this.setThumbBoxPosition();
35467             this.setCanvasPosition();
35468         }
35469         
35470         this.draw();
35471         
35472         this.fireEvent('rotate', this, 'left');
35473         
35474     },
35475     
35476     onRotateRight : function(e)
35477     {
35478         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35479             
35480             var minScale = this.thumbEl.getWidth() / this.minWidth;
35481         
35482             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35483             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35484             
35485             this.startScale = this.scale;
35486             
35487             while (this.getScaleLevel() < minScale){
35488             
35489                 this.scale = this.scale + 1;
35490                 
35491                 if(!this.zoomable()){
35492                     break;
35493                 }
35494                 
35495                 if(
35496                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35497                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35498                 ){
35499                     continue;
35500                 }
35501                 
35502                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35503
35504                 this.draw();
35505                 
35506                 return;
35507             }
35508             
35509             this.scale = this.startScale;
35510             
35511             this.onRotateFail();
35512             
35513             return false;
35514         }
35515         
35516         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35517
35518         if(this.isDocument){
35519             this.setThumbBoxSize();
35520             this.setThumbBoxPosition();
35521             this.setCanvasPosition();
35522         }
35523         
35524         this.draw();
35525         
35526         this.fireEvent('rotate', this, 'right');
35527     },
35528     
35529     onRotateFail : function()
35530     {
35531         this.errorEl.show(true);
35532         
35533         var _this = this;
35534         
35535         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
35536     },
35537     
35538     draw : function()
35539     {
35540         this.previewEl.dom.innerHTML = '';
35541         
35542         var canvasEl = document.createElement("canvas");
35543         
35544         var contextEl = canvasEl.getContext("2d");
35545         
35546         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35547         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35548         var center = this.imageEl.OriginWidth / 2;
35549         
35550         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
35551             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35552             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35553             center = this.imageEl.OriginHeight / 2;
35554         }
35555         
35556         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
35557         
35558         contextEl.translate(center, center);
35559         contextEl.rotate(this.rotate * Math.PI / 180);
35560
35561         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35562         
35563         this.canvasEl = document.createElement("canvas");
35564         
35565         this.contextEl = this.canvasEl.getContext("2d");
35566         
35567         switch (this.rotate) {
35568             case 0 :
35569                 
35570                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35571                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35572                 
35573                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35574                 
35575                 break;
35576             case 90 : 
35577                 
35578                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35579                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35580                 
35581                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35582                     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);
35583                     break;
35584                 }
35585                 
35586                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35587                 
35588                 break;
35589             case 180 :
35590                 
35591                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35592                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35593                 
35594                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35595                     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);
35596                     break;
35597                 }
35598                 
35599                 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);
35600                 
35601                 break;
35602             case 270 :
35603                 
35604                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35605                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35606         
35607                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35608                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35609                     break;
35610                 }
35611                 
35612                 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);
35613                 
35614                 break;
35615             default : 
35616                 break;
35617         }
35618         
35619         this.previewEl.appendChild(this.canvasEl);
35620         
35621         this.setCanvasPosition();
35622     },
35623     
35624     crop : function()
35625     {
35626         if(!this.canvasLoaded){
35627             return;
35628         }
35629         
35630         var imageCanvas = document.createElement("canvas");
35631         
35632         var imageContext = imageCanvas.getContext("2d");
35633         
35634         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
35635         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
35636         
35637         var center = imageCanvas.width / 2;
35638         
35639         imageContext.translate(center, center);
35640         
35641         imageContext.rotate(this.rotate * Math.PI / 180);
35642         
35643         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35644         
35645         var canvas = document.createElement("canvas");
35646         
35647         var context = canvas.getContext("2d");
35648                 
35649         canvas.width = this.minWidth;
35650         canvas.height = this.minHeight;
35651
35652         switch (this.rotate) {
35653             case 0 :
35654                 
35655                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
35656                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
35657                 
35658                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35659                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35660                 
35661                 var targetWidth = this.minWidth - 2 * x;
35662                 var targetHeight = this.minHeight - 2 * y;
35663                 
35664                 var scale = 1;
35665                 
35666                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35667                     scale = targetWidth / width;
35668                 }
35669                 
35670                 if(x > 0 && y == 0){
35671                     scale = targetHeight / height;
35672                 }
35673                 
35674                 if(x > 0 && y > 0){
35675                     scale = targetWidth / width;
35676                     
35677                     if(width < height){
35678                         scale = targetHeight / height;
35679                     }
35680                 }
35681                 
35682                 context.scale(scale, scale);
35683                 
35684                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35685                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35686
35687                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35688                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35689
35690                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35691                 
35692                 break;
35693             case 90 : 
35694                 
35695                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
35696                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
35697                 
35698                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35699                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35700                 
35701                 var targetWidth = this.minWidth - 2 * x;
35702                 var targetHeight = this.minHeight - 2 * y;
35703                 
35704                 var scale = 1;
35705                 
35706                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35707                     scale = targetWidth / width;
35708                 }
35709                 
35710                 if(x > 0 && y == 0){
35711                     scale = targetHeight / height;
35712                 }
35713                 
35714                 if(x > 0 && y > 0){
35715                     scale = targetWidth / width;
35716                     
35717                     if(width < height){
35718                         scale = targetHeight / height;
35719                     }
35720                 }
35721                 
35722                 context.scale(scale, scale);
35723                 
35724                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35725                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35726
35727                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35728                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35729                 
35730                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
35731                 
35732                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35733                 
35734                 break;
35735             case 180 :
35736                 
35737                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
35738                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
35739                 
35740                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35741                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35742                 
35743                 var targetWidth = this.minWidth - 2 * x;
35744                 var targetHeight = this.minHeight - 2 * y;
35745                 
35746                 var scale = 1;
35747                 
35748                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35749                     scale = targetWidth / width;
35750                 }
35751                 
35752                 if(x > 0 && y == 0){
35753                     scale = targetHeight / height;
35754                 }
35755                 
35756                 if(x > 0 && y > 0){
35757                     scale = targetWidth / width;
35758                     
35759                     if(width < height){
35760                         scale = targetHeight / height;
35761                     }
35762                 }
35763                 
35764                 context.scale(scale, scale);
35765                 
35766                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35767                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35768
35769                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35770                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35771
35772                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35773                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
35774                 
35775                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35776                 
35777                 break;
35778             case 270 :
35779                 
35780                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
35781                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
35782                 
35783                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35784                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35785                 
35786                 var targetWidth = this.minWidth - 2 * x;
35787                 var targetHeight = this.minHeight - 2 * y;
35788                 
35789                 var scale = 1;
35790                 
35791                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35792                     scale = targetWidth / width;
35793                 }
35794                 
35795                 if(x > 0 && y == 0){
35796                     scale = targetHeight / height;
35797                 }
35798                 
35799                 if(x > 0 && y > 0){
35800                     scale = targetWidth / width;
35801                     
35802                     if(width < height){
35803                         scale = targetHeight / height;
35804                     }
35805                 }
35806                 
35807                 context.scale(scale, scale);
35808                 
35809                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35810                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35811
35812                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35813                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35814                 
35815                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35816                 
35817                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35818                 
35819                 break;
35820             default : 
35821                 break;
35822         }
35823         
35824         this.cropData = canvas.toDataURL(this.cropType);
35825         
35826         if(this.fireEvent('crop', this, this.cropData) !== false){
35827             this.process(this.file, this.cropData);
35828         }
35829         
35830         return;
35831         
35832     },
35833     
35834     setThumbBoxSize : function()
35835     {
35836         var width, height;
35837         
35838         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
35839             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
35840             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
35841             
35842             this.minWidth = width;
35843             this.minHeight = height;
35844             
35845             if(this.rotate == 90 || this.rotate == 270){
35846                 this.minWidth = height;
35847                 this.minHeight = width;
35848             }
35849         }
35850         
35851         height = 300;
35852         width = Math.ceil(this.minWidth * height / this.minHeight);
35853         
35854         if(this.minWidth > this.minHeight){
35855             width = 300;
35856             height = Math.ceil(this.minHeight * width / this.minWidth);
35857         }
35858         
35859         this.thumbEl.setStyle({
35860             width : width + 'px',
35861             height : height + 'px'
35862         });
35863
35864         return;
35865             
35866     },
35867     
35868     setThumbBoxPosition : function()
35869     {
35870         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
35871         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
35872         
35873         this.thumbEl.setLeft(x);
35874         this.thumbEl.setTop(y);
35875         
35876     },
35877     
35878     baseRotateLevel : function()
35879     {
35880         this.baseRotate = 1;
35881         
35882         if(
35883                 typeof(this.exif) != 'undefined' &&
35884                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
35885                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
35886         ){
35887             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
35888         }
35889         
35890         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
35891         
35892     },
35893     
35894     baseScaleLevel : function()
35895     {
35896         var width, height;
35897         
35898         if(this.isDocument){
35899             
35900             if(this.baseRotate == 6 || this.baseRotate == 8){
35901             
35902                 height = this.thumbEl.getHeight();
35903                 this.baseScale = height / this.imageEl.OriginWidth;
35904
35905                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
35906                     width = this.thumbEl.getWidth();
35907                     this.baseScale = width / this.imageEl.OriginHeight;
35908                 }
35909
35910                 return;
35911             }
35912
35913             height = this.thumbEl.getHeight();
35914             this.baseScale = height / this.imageEl.OriginHeight;
35915
35916             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
35917                 width = this.thumbEl.getWidth();
35918                 this.baseScale = width / this.imageEl.OriginWidth;
35919             }
35920
35921             return;
35922         }
35923         
35924         if(this.baseRotate == 6 || this.baseRotate == 8){
35925             
35926             width = this.thumbEl.getHeight();
35927             this.baseScale = width / this.imageEl.OriginHeight;
35928             
35929             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
35930                 height = this.thumbEl.getWidth();
35931                 this.baseScale = height / this.imageEl.OriginHeight;
35932             }
35933             
35934             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35935                 height = this.thumbEl.getWidth();
35936                 this.baseScale = height / this.imageEl.OriginHeight;
35937                 
35938                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
35939                     width = this.thumbEl.getHeight();
35940                     this.baseScale = width / this.imageEl.OriginWidth;
35941                 }
35942             }
35943             
35944             return;
35945         }
35946         
35947         width = this.thumbEl.getWidth();
35948         this.baseScale = width / this.imageEl.OriginWidth;
35949         
35950         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
35951             height = this.thumbEl.getHeight();
35952             this.baseScale = height / this.imageEl.OriginHeight;
35953         }
35954         
35955         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35956             
35957             height = this.thumbEl.getHeight();
35958             this.baseScale = height / this.imageEl.OriginHeight;
35959             
35960             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
35961                 width = this.thumbEl.getWidth();
35962                 this.baseScale = width / this.imageEl.OriginWidth;
35963             }
35964             
35965         }
35966         
35967         return;
35968     },
35969     
35970     getScaleLevel : function()
35971     {
35972         return this.baseScale * Math.pow(1.1, this.scale);
35973     },
35974     
35975     onTouchStart : function(e)
35976     {
35977         if(!this.canvasLoaded){
35978             this.beforeSelectFile(e);
35979             return;
35980         }
35981         
35982         var touches = e.browserEvent.touches;
35983         
35984         if(!touches){
35985             return;
35986         }
35987         
35988         if(touches.length == 1){
35989             this.onMouseDown(e);
35990             return;
35991         }
35992         
35993         if(touches.length != 2){
35994             return;
35995         }
35996         
35997         var coords = [];
35998         
35999         for(var i = 0, finger; finger = touches[i]; i++){
36000             coords.push(finger.pageX, finger.pageY);
36001         }
36002         
36003         var x = Math.pow(coords[0] - coords[2], 2);
36004         var y = Math.pow(coords[1] - coords[3], 2);
36005         
36006         this.startDistance = Math.sqrt(x + y);
36007         
36008         this.startScale = this.scale;
36009         
36010         this.pinching = true;
36011         this.dragable = false;
36012         
36013     },
36014     
36015     onTouchMove : function(e)
36016     {
36017         if(!this.pinching && !this.dragable){
36018             return;
36019         }
36020         
36021         var touches = e.browserEvent.touches;
36022         
36023         if(!touches){
36024             return;
36025         }
36026         
36027         if(this.dragable){
36028             this.onMouseMove(e);
36029             return;
36030         }
36031         
36032         var coords = [];
36033         
36034         for(var i = 0, finger; finger = touches[i]; i++){
36035             coords.push(finger.pageX, finger.pageY);
36036         }
36037         
36038         var x = Math.pow(coords[0] - coords[2], 2);
36039         var y = Math.pow(coords[1] - coords[3], 2);
36040         
36041         this.endDistance = Math.sqrt(x + y);
36042         
36043         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36044         
36045         if(!this.zoomable()){
36046             this.scale = this.startScale;
36047             return;
36048         }
36049         
36050         this.draw();
36051         
36052     },
36053     
36054     onTouchEnd : function(e)
36055     {
36056         this.pinching = false;
36057         this.dragable = false;
36058         
36059     },
36060     
36061     process : function(file, crop)
36062     {
36063         if(this.loadMask){
36064             this.maskEl.mask(this.loadingText);
36065         }
36066         
36067         this.xhr = new XMLHttpRequest();
36068         
36069         file.xhr = this.xhr;
36070
36071         this.xhr.open(this.method, this.url, true);
36072         
36073         var headers = {
36074             "Accept": "application/json",
36075             "Cache-Control": "no-cache",
36076             "X-Requested-With": "XMLHttpRequest"
36077         };
36078         
36079         for (var headerName in headers) {
36080             var headerValue = headers[headerName];
36081             if (headerValue) {
36082                 this.xhr.setRequestHeader(headerName, headerValue);
36083             }
36084         }
36085         
36086         var _this = this;
36087         
36088         this.xhr.onload = function()
36089         {
36090             _this.xhrOnLoad(_this.xhr);
36091         }
36092         
36093         this.xhr.onerror = function()
36094         {
36095             _this.xhrOnError(_this.xhr);
36096         }
36097         
36098         var formData = new FormData();
36099
36100         formData.append('returnHTML', 'NO');
36101         
36102         if(crop){
36103             formData.append('crop', crop);
36104         }
36105         
36106         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36107             formData.append(this.paramName, file, file.name);
36108         }
36109         
36110         if(typeof(file.filename) != 'undefined'){
36111             formData.append('filename', file.filename);
36112         }
36113         
36114         if(typeof(file.mimetype) != 'undefined'){
36115             formData.append('mimetype', file.mimetype);
36116         }
36117         
36118         if(this.fireEvent('arrange', this, formData) != false){
36119             this.xhr.send(formData);
36120         };
36121     },
36122     
36123     xhrOnLoad : function(xhr)
36124     {
36125         if(this.loadMask){
36126             this.maskEl.unmask();
36127         }
36128         
36129         if (xhr.readyState !== 4) {
36130             this.fireEvent('exception', this, xhr);
36131             return;
36132         }
36133
36134         var response = Roo.decode(xhr.responseText);
36135         
36136         if(!response.success){
36137             this.fireEvent('exception', this, xhr);
36138             return;
36139         }
36140         
36141         var response = Roo.decode(xhr.responseText);
36142         
36143         this.fireEvent('upload', this, response);
36144         
36145     },
36146     
36147     xhrOnError : function()
36148     {
36149         if(this.loadMask){
36150             this.maskEl.unmask();
36151         }
36152         
36153         Roo.log('xhr on error');
36154         
36155         var response = Roo.decode(xhr.responseText);
36156           
36157         Roo.log(response);
36158         
36159     },
36160     
36161     prepare : function(file)
36162     {   
36163         if(this.loadMask){
36164             this.maskEl.mask(this.loadingText);
36165         }
36166         
36167         this.file = false;
36168         this.exif = {};
36169         
36170         if(typeof(file) === 'string'){
36171             this.loadCanvas(file);
36172             return;
36173         }
36174         
36175         if(!file || !this.urlAPI){
36176             return;
36177         }
36178         
36179         this.file = file;
36180         this.cropType = file.type;
36181         
36182         var _this = this;
36183         
36184         if(this.fireEvent('prepare', this, this.file) != false){
36185             
36186             var reader = new FileReader();
36187             
36188             reader.onload = function (e) {
36189                 if (e.target.error) {
36190                     Roo.log(e.target.error);
36191                     return;
36192                 }
36193                 
36194                 var buffer = e.target.result,
36195                     dataView = new DataView(buffer),
36196                     offset = 2,
36197                     maxOffset = dataView.byteLength - 4,
36198                     markerBytes,
36199                     markerLength;
36200                 
36201                 if (dataView.getUint16(0) === 0xffd8) {
36202                     while (offset < maxOffset) {
36203                         markerBytes = dataView.getUint16(offset);
36204                         
36205                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
36206                             markerLength = dataView.getUint16(offset + 2) + 2;
36207                             if (offset + markerLength > dataView.byteLength) {
36208                                 Roo.log('Invalid meta data: Invalid segment size.');
36209                                 break;
36210                             }
36211                             
36212                             if(markerBytes == 0xffe1){
36213                                 _this.parseExifData(
36214                                     dataView,
36215                                     offset,
36216                                     markerLength
36217                                 );
36218                             }
36219                             
36220                             offset += markerLength;
36221                             
36222                             continue;
36223                         }
36224                         
36225                         break;
36226                     }
36227                     
36228                 }
36229                 
36230                 var url = _this.urlAPI.createObjectURL(_this.file);
36231                 
36232                 _this.loadCanvas(url);
36233                 
36234                 return;
36235             }
36236             
36237             reader.readAsArrayBuffer(this.file);
36238             
36239         }
36240         
36241     },
36242     
36243     parseExifData : function(dataView, offset, length)
36244     {
36245         var tiffOffset = offset + 10,
36246             littleEndian,
36247             dirOffset;
36248     
36249         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36250             // No Exif data, might be XMP data instead
36251             return;
36252         }
36253         
36254         // Check for the ASCII code for "Exif" (0x45786966):
36255         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36256             // No Exif data, might be XMP data instead
36257             return;
36258         }
36259         if (tiffOffset + 8 > dataView.byteLength) {
36260             Roo.log('Invalid Exif data: Invalid segment size.');
36261             return;
36262         }
36263         // Check for the two null bytes:
36264         if (dataView.getUint16(offset + 8) !== 0x0000) {
36265             Roo.log('Invalid Exif data: Missing byte alignment offset.');
36266             return;
36267         }
36268         // Check the byte alignment:
36269         switch (dataView.getUint16(tiffOffset)) {
36270         case 0x4949:
36271             littleEndian = true;
36272             break;
36273         case 0x4D4D:
36274             littleEndian = false;
36275             break;
36276         default:
36277             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
36278             return;
36279         }
36280         // Check for the TIFF tag marker (0x002A):
36281         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
36282             Roo.log('Invalid Exif data: Missing TIFF marker.');
36283             return;
36284         }
36285         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
36286         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
36287         
36288         this.parseExifTags(
36289             dataView,
36290             tiffOffset,
36291             tiffOffset + dirOffset,
36292             littleEndian
36293         );
36294     },
36295     
36296     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
36297     {
36298         var tagsNumber,
36299             dirEndOffset,
36300             i;
36301         if (dirOffset + 6 > dataView.byteLength) {
36302             Roo.log('Invalid Exif data: Invalid directory offset.');
36303             return;
36304         }
36305         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
36306         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
36307         if (dirEndOffset + 4 > dataView.byteLength) {
36308             Roo.log('Invalid Exif data: Invalid directory size.');
36309             return;
36310         }
36311         for (i = 0; i < tagsNumber; i += 1) {
36312             this.parseExifTag(
36313                 dataView,
36314                 tiffOffset,
36315                 dirOffset + 2 + 12 * i, // tag offset
36316                 littleEndian
36317             );
36318         }
36319         // Return the offset to the next directory:
36320         return dataView.getUint32(dirEndOffset, littleEndian);
36321     },
36322     
36323     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
36324     {
36325         var tag = dataView.getUint16(offset, littleEndian);
36326         
36327         this.exif[tag] = this.getExifValue(
36328             dataView,
36329             tiffOffset,
36330             offset,
36331             dataView.getUint16(offset + 2, littleEndian), // tag type
36332             dataView.getUint32(offset + 4, littleEndian), // tag length
36333             littleEndian
36334         );
36335     },
36336     
36337     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
36338     {
36339         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
36340             tagSize,
36341             dataOffset,
36342             values,
36343             i,
36344             str,
36345             c;
36346     
36347         if (!tagType) {
36348             Roo.log('Invalid Exif data: Invalid tag type.');
36349             return;
36350         }
36351         
36352         tagSize = tagType.size * length;
36353         // Determine if the value is contained in the dataOffset bytes,
36354         // or if the value at the dataOffset is a pointer to the actual data:
36355         dataOffset = tagSize > 4 ?
36356                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
36357         if (dataOffset + tagSize > dataView.byteLength) {
36358             Roo.log('Invalid Exif data: Invalid data offset.');
36359             return;
36360         }
36361         if (length === 1) {
36362             return tagType.getValue(dataView, dataOffset, littleEndian);
36363         }
36364         values = [];
36365         for (i = 0; i < length; i += 1) {
36366             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
36367         }
36368         
36369         if (tagType.ascii) {
36370             str = '';
36371             // Concatenate the chars:
36372             for (i = 0; i < values.length; i += 1) {
36373                 c = values[i];
36374                 // Ignore the terminating NULL byte(s):
36375                 if (c === '\u0000') {
36376                     break;
36377                 }
36378                 str += c;
36379             }
36380             return str;
36381         }
36382         return values;
36383     }
36384     
36385 });
36386
36387 Roo.apply(Roo.bootstrap.UploadCropbox, {
36388     tags : {
36389         'Orientation': 0x0112
36390     },
36391     
36392     Orientation: {
36393             1: 0, //'top-left',
36394 //            2: 'top-right',
36395             3: 180, //'bottom-right',
36396 //            4: 'bottom-left',
36397 //            5: 'left-top',
36398             6: 90, //'right-top',
36399 //            7: 'right-bottom',
36400             8: 270 //'left-bottom'
36401     },
36402     
36403     exifTagTypes : {
36404         // byte, 8-bit unsigned int:
36405         1: {
36406             getValue: function (dataView, dataOffset) {
36407                 return dataView.getUint8(dataOffset);
36408             },
36409             size: 1
36410         },
36411         // ascii, 8-bit byte:
36412         2: {
36413             getValue: function (dataView, dataOffset) {
36414                 return String.fromCharCode(dataView.getUint8(dataOffset));
36415             },
36416             size: 1,
36417             ascii: true
36418         },
36419         // short, 16 bit int:
36420         3: {
36421             getValue: function (dataView, dataOffset, littleEndian) {
36422                 return dataView.getUint16(dataOffset, littleEndian);
36423             },
36424             size: 2
36425         },
36426         // long, 32 bit int:
36427         4: {
36428             getValue: function (dataView, dataOffset, littleEndian) {
36429                 return dataView.getUint32(dataOffset, littleEndian);
36430             },
36431             size: 4
36432         },
36433         // rational = two long values, first is numerator, second is denominator:
36434         5: {
36435             getValue: function (dataView, dataOffset, littleEndian) {
36436                 return dataView.getUint32(dataOffset, littleEndian) /
36437                     dataView.getUint32(dataOffset + 4, littleEndian);
36438             },
36439             size: 8
36440         },
36441         // slong, 32 bit signed int:
36442         9: {
36443             getValue: function (dataView, dataOffset, littleEndian) {
36444                 return dataView.getInt32(dataOffset, littleEndian);
36445             },
36446             size: 4
36447         },
36448         // srational, two slongs, first is numerator, second is denominator:
36449         10: {
36450             getValue: function (dataView, dataOffset, littleEndian) {
36451                 return dataView.getInt32(dataOffset, littleEndian) /
36452                     dataView.getInt32(dataOffset + 4, littleEndian);
36453             },
36454             size: 8
36455         }
36456     },
36457     
36458     footer : {
36459         STANDARD : [
36460             {
36461                 tag : 'div',
36462                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36463                 action : 'rotate-left',
36464                 cn : [
36465                     {
36466                         tag : 'button',
36467                         cls : 'btn btn-default',
36468                         html : '<i class="fa fa-undo"></i>'
36469                     }
36470                 ]
36471             },
36472             {
36473                 tag : 'div',
36474                 cls : 'btn-group roo-upload-cropbox-picture',
36475                 action : 'picture',
36476                 cn : [
36477                     {
36478                         tag : 'button',
36479                         cls : 'btn btn-default',
36480                         html : '<i class="fa fa-picture-o"></i>'
36481                     }
36482                 ]
36483             },
36484             {
36485                 tag : 'div',
36486                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36487                 action : 'rotate-right',
36488                 cn : [
36489                     {
36490                         tag : 'button',
36491                         cls : 'btn btn-default',
36492                         html : '<i class="fa fa-repeat"></i>'
36493                     }
36494                 ]
36495             }
36496         ],
36497         DOCUMENT : [
36498             {
36499                 tag : 'div',
36500                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36501                 action : 'rotate-left',
36502                 cn : [
36503                     {
36504                         tag : 'button',
36505                         cls : 'btn btn-default',
36506                         html : '<i class="fa fa-undo"></i>'
36507                     }
36508                 ]
36509             },
36510             {
36511                 tag : 'div',
36512                 cls : 'btn-group roo-upload-cropbox-download',
36513                 action : 'download',
36514                 cn : [
36515                     {
36516                         tag : 'button',
36517                         cls : 'btn btn-default',
36518                         html : '<i class="fa fa-download"></i>'
36519                     }
36520                 ]
36521             },
36522             {
36523                 tag : 'div',
36524                 cls : 'btn-group roo-upload-cropbox-crop',
36525                 action : 'crop',
36526                 cn : [
36527                     {
36528                         tag : 'button',
36529                         cls : 'btn btn-default',
36530                         html : '<i class="fa fa-crop"></i>'
36531                     }
36532                 ]
36533             },
36534             {
36535                 tag : 'div',
36536                 cls : 'btn-group roo-upload-cropbox-trash',
36537                 action : 'trash',
36538                 cn : [
36539                     {
36540                         tag : 'button',
36541                         cls : 'btn btn-default',
36542                         html : '<i class="fa fa-trash"></i>'
36543                     }
36544                 ]
36545             },
36546             {
36547                 tag : 'div',
36548                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36549                 action : 'rotate-right',
36550                 cn : [
36551                     {
36552                         tag : 'button',
36553                         cls : 'btn btn-default',
36554                         html : '<i class="fa fa-repeat"></i>'
36555                     }
36556                 ]
36557             }
36558         ],
36559         ROTATOR : [
36560             {
36561                 tag : 'div',
36562                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36563                 action : 'rotate-left',
36564                 cn : [
36565                     {
36566                         tag : 'button',
36567                         cls : 'btn btn-default',
36568                         html : '<i class="fa fa-undo"></i>'
36569                     }
36570                 ]
36571             },
36572             {
36573                 tag : 'div',
36574                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36575                 action : 'rotate-right',
36576                 cn : [
36577                     {
36578                         tag : 'button',
36579                         cls : 'btn btn-default',
36580                         html : '<i class="fa fa-repeat"></i>'
36581                     }
36582                 ]
36583             }
36584         ]
36585     }
36586 });
36587
36588 /*
36589 * Licence: LGPL
36590 */
36591
36592 /**
36593  * @class Roo.bootstrap.DocumentManager
36594  * @extends Roo.bootstrap.Component
36595  * Bootstrap DocumentManager class
36596  * @cfg {String} paramName default 'imageUpload'
36597  * @cfg {String} toolTipName default 'filename'
36598  * @cfg {String} method default POST
36599  * @cfg {String} url action url
36600  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
36601  * @cfg {Boolean} multiple multiple upload default true
36602  * @cfg {Number} thumbSize default 300
36603  * @cfg {String} fieldLabel
36604  * @cfg {Number} labelWidth default 4
36605  * @cfg {String} labelAlign (left|top) default left
36606  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
36607 * @cfg {Number} labellg set the width of label (1-12)
36608  * @cfg {Number} labelmd set the width of label (1-12)
36609  * @cfg {Number} labelsm set the width of label (1-12)
36610  * @cfg {Number} labelxs set the width of label (1-12)
36611  * 
36612  * @constructor
36613  * Create a new DocumentManager
36614  * @param {Object} config The config object
36615  */
36616
36617 Roo.bootstrap.DocumentManager = function(config){
36618     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
36619     
36620     this.files = [];
36621     this.delegates = [];
36622     
36623     this.addEvents({
36624         /**
36625          * @event initial
36626          * Fire when initial the DocumentManager
36627          * @param {Roo.bootstrap.DocumentManager} this
36628          */
36629         "initial" : true,
36630         /**
36631          * @event inspect
36632          * inspect selected file
36633          * @param {Roo.bootstrap.DocumentManager} this
36634          * @param {File} file
36635          */
36636         "inspect" : true,
36637         /**
36638          * @event exception
36639          * Fire when xhr load exception
36640          * @param {Roo.bootstrap.DocumentManager} this
36641          * @param {XMLHttpRequest} xhr
36642          */
36643         "exception" : true,
36644         /**
36645          * @event afterupload
36646          * Fire when xhr load exception
36647          * @param {Roo.bootstrap.DocumentManager} this
36648          * @param {XMLHttpRequest} xhr
36649          */
36650         "afterupload" : true,
36651         /**
36652          * @event prepare
36653          * prepare the form data
36654          * @param {Roo.bootstrap.DocumentManager} this
36655          * @param {Object} formData
36656          */
36657         "prepare" : true,
36658         /**
36659          * @event remove
36660          * Fire when remove the file
36661          * @param {Roo.bootstrap.DocumentManager} this
36662          * @param {Object} file
36663          */
36664         "remove" : true,
36665         /**
36666          * @event refresh
36667          * Fire after refresh the file
36668          * @param {Roo.bootstrap.DocumentManager} this
36669          */
36670         "refresh" : true,
36671         /**
36672          * @event click
36673          * Fire after click the image
36674          * @param {Roo.bootstrap.DocumentManager} this
36675          * @param {Object} file
36676          */
36677         "click" : true,
36678         /**
36679          * @event edit
36680          * Fire when upload a image and editable set to true
36681          * @param {Roo.bootstrap.DocumentManager} this
36682          * @param {Object} file
36683          */
36684         "edit" : true,
36685         /**
36686          * @event beforeselectfile
36687          * Fire before select file
36688          * @param {Roo.bootstrap.DocumentManager} this
36689          */
36690         "beforeselectfile" : true,
36691         /**
36692          * @event process
36693          * Fire before process file
36694          * @param {Roo.bootstrap.DocumentManager} this
36695          * @param {Object} file
36696          */
36697         "process" : true,
36698         /**
36699          * @event previewrendered
36700          * Fire when preview rendered
36701          * @param {Roo.bootstrap.DocumentManager} this
36702          * @param {Object} file
36703          */
36704         "previewrendered" : true,
36705         /**
36706          */
36707         "previewResize" : true
36708         
36709     });
36710 };
36711
36712 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
36713     
36714     boxes : 0,
36715     inputName : '',
36716     thumbSize : 300,
36717     multiple : true,
36718     files : false,
36719     method : 'POST',
36720     url : '',
36721     paramName : 'imageUpload',
36722     toolTipName : 'filename',
36723     fieldLabel : '',
36724     labelWidth : 4,
36725     labelAlign : 'left',
36726     editable : true,
36727     delegates : false,
36728     xhr : false, 
36729     
36730     labellg : 0,
36731     labelmd : 0,
36732     labelsm : 0,
36733     labelxs : 0,
36734     
36735     getAutoCreate : function()
36736     {   
36737         var managerWidget = {
36738             tag : 'div',
36739             cls : 'roo-document-manager',
36740             cn : [
36741                 {
36742                     tag : 'input',
36743                     cls : 'roo-document-manager-selector',
36744                     type : 'file'
36745                 },
36746                 {
36747                     tag : 'div',
36748                     cls : 'roo-document-manager-uploader',
36749                     cn : [
36750                         {
36751                             tag : 'div',
36752                             cls : 'roo-document-manager-upload-btn',
36753                             html : '<i class="fa fa-plus"></i>'
36754                         }
36755                     ]
36756                     
36757                 }
36758             ]
36759         };
36760         
36761         var content = [
36762             {
36763                 tag : 'div',
36764                 cls : 'column col-md-12',
36765                 cn : managerWidget
36766             }
36767         ];
36768         
36769         if(this.fieldLabel.length){
36770             
36771             content = [
36772                 {
36773                     tag : 'div',
36774                     cls : 'column col-md-12',
36775                     html : this.fieldLabel
36776                 },
36777                 {
36778                     tag : 'div',
36779                     cls : 'column col-md-12',
36780                     cn : managerWidget
36781                 }
36782             ];
36783
36784             if(this.labelAlign == 'left'){
36785                 content = [
36786                     {
36787                         tag : 'div',
36788                         cls : 'column',
36789                         html : this.fieldLabel
36790                     },
36791                     {
36792                         tag : 'div',
36793                         cls : 'column',
36794                         cn : managerWidget
36795                     }
36796                 ];
36797                 
36798                 if(this.labelWidth > 12){
36799                     content[0].style = "width: " + this.labelWidth + 'px';
36800                 }
36801
36802                 if(this.labelWidth < 13 && this.labelmd == 0){
36803                     this.labelmd = this.labelWidth;
36804                 }
36805
36806                 if(this.labellg > 0){
36807                     content[0].cls += ' col-lg-' + this.labellg;
36808                     content[1].cls += ' col-lg-' + (12 - this.labellg);
36809                 }
36810
36811                 if(this.labelmd > 0){
36812                     content[0].cls += ' col-md-' + this.labelmd;
36813                     content[1].cls += ' col-md-' + (12 - this.labelmd);
36814                 }
36815
36816                 if(this.labelsm > 0){
36817                     content[0].cls += ' col-sm-' + this.labelsm;
36818                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
36819                 }
36820
36821                 if(this.labelxs > 0){
36822                     content[0].cls += ' col-xs-' + this.labelxs;
36823                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
36824                 }
36825                 
36826             }
36827         }
36828         
36829         var cfg = {
36830             tag : 'div',
36831             cls : 'row clearfix',
36832             cn : content
36833         };
36834         
36835         return cfg;
36836         
36837     },
36838     
36839     initEvents : function()
36840     {
36841         this.managerEl = this.el.select('.roo-document-manager', true).first();
36842         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36843         
36844         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
36845         this.selectorEl.hide();
36846         
36847         if(this.multiple){
36848             this.selectorEl.attr('multiple', 'multiple');
36849         }
36850         
36851         this.selectorEl.on('change', this.onFileSelected, this);
36852         
36853         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
36854         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36855         
36856         this.uploader.on('click', this.onUploaderClick, this);
36857         
36858         this.renderProgressDialog();
36859         
36860         var _this = this;
36861         
36862         window.addEventListener("resize", function() { _this.refresh(); } );
36863         
36864         this.fireEvent('initial', this);
36865     },
36866     
36867     renderProgressDialog : function()
36868     {
36869         var _this = this;
36870         
36871         this.progressDialog = new Roo.bootstrap.Modal({
36872             cls : 'roo-document-manager-progress-dialog',
36873             allow_close : false,
36874             animate : false,
36875             title : '',
36876             buttons : [
36877                 {
36878                     name  :'cancel',
36879                     weight : 'danger',
36880                     html : 'Cancel'
36881                 }
36882             ], 
36883             listeners : { 
36884                 btnclick : function() {
36885                     _this.uploadCancel();
36886                     this.hide();
36887                 }
36888             }
36889         });
36890          
36891         this.progressDialog.render(Roo.get(document.body));
36892          
36893         this.progress = new Roo.bootstrap.Progress({
36894             cls : 'roo-document-manager-progress',
36895             active : true,
36896             striped : true
36897         });
36898         
36899         this.progress.render(this.progressDialog.getChildContainer());
36900         
36901         this.progressBar = new Roo.bootstrap.ProgressBar({
36902             cls : 'roo-document-manager-progress-bar',
36903             aria_valuenow : 0,
36904             aria_valuemin : 0,
36905             aria_valuemax : 12,
36906             panel : 'success'
36907         });
36908         
36909         this.progressBar.render(this.progress.getChildContainer());
36910     },
36911     
36912     onUploaderClick : function(e)
36913     {
36914         e.preventDefault();
36915      
36916         if(this.fireEvent('beforeselectfile', this) != false){
36917             this.selectorEl.dom.click();
36918         }
36919         
36920     },
36921     
36922     onFileSelected : function(e)
36923     {
36924         e.preventDefault();
36925         
36926         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36927             return;
36928         }
36929         
36930         Roo.each(this.selectorEl.dom.files, function(file){
36931             if(this.fireEvent('inspect', this, file) != false){
36932                 this.files.push(file);
36933             }
36934         }, this);
36935         
36936         this.queue();
36937         
36938     },
36939     
36940     queue : function()
36941     {
36942         this.selectorEl.dom.value = '';
36943         
36944         if(!this.files || !this.files.length){
36945             return;
36946         }
36947         
36948         if(this.boxes > 0 && this.files.length > this.boxes){
36949             this.files = this.files.slice(0, this.boxes);
36950         }
36951         
36952         this.uploader.show();
36953         
36954         if(this.boxes > 0 && this.files.length > this.boxes - 1){
36955             this.uploader.hide();
36956         }
36957         
36958         var _this = this;
36959         
36960         var files = [];
36961         
36962         var docs = [];
36963         
36964         Roo.each(this.files, function(file){
36965             
36966             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
36967                 var f = this.renderPreview(file);
36968                 files.push(f);
36969                 return;
36970             }
36971             
36972             if(file.type.indexOf('image') != -1){
36973                 this.delegates.push(
36974                     (function(){
36975                         _this.process(file);
36976                     }).createDelegate(this)
36977                 );
36978         
36979                 return;
36980             }
36981             
36982             docs.push(
36983                 (function(){
36984                     _this.process(file);
36985                 }).createDelegate(this)
36986             );
36987             
36988         }, this);
36989         
36990         this.files = files;
36991         
36992         this.delegates = this.delegates.concat(docs);
36993         
36994         if(!this.delegates.length){
36995             this.refresh();
36996             return;
36997         }
36998         
36999         this.progressBar.aria_valuemax = this.delegates.length;
37000         
37001         this.arrange();
37002         
37003         return;
37004     },
37005     
37006     arrange : function()
37007     {
37008         if(!this.delegates.length){
37009             this.progressDialog.hide();
37010             this.refresh();
37011             return;
37012         }
37013         
37014         var delegate = this.delegates.shift();
37015         
37016         this.progressDialog.show();
37017         
37018         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37019         
37020         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37021         
37022         delegate();
37023     },
37024     
37025     refresh : function()
37026     {
37027         this.uploader.show();
37028         
37029         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37030             this.uploader.hide();
37031         }
37032         
37033         Roo.isTouch ? this.closable(false) : this.closable(true);
37034         
37035         this.fireEvent('refresh', this);
37036     },
37037     
37038     onRemove : function(e, el, o)
37039     {
37040         e.preventDefault();
37041         
37042         this.fireEvent('remove', this, o);
37043         
37044     },
37045     
37046     remove : function(o)
37047     {
37048         var files = [];
37049         
37050         Roo.each(this.files, function(file){
37051             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37052                 files.push(file);
37053                 return;
37054             }
37055
37056             o.target.remove();
37057
37058         }, this);
37059         
37060         this.files = files;
37061         
37062         this.refresh();
37063     },
37064     
37065     clear : function()
37066     {
37067         Roo.each(this.files, function(file){
37068             if(!file.target){
37069                 return;
37070             }
37071             
37072             file.target.remove();
37073
37074         }, this);
37075         
37076         this.files = [];
37077         
37078         this.refresh();
37079     },
37080     
37081     onClick : function(e, el, o)
37082     {
37083         e.preventDefault();
37084         
37085         this.fireEvent('click', this, o);
37086         
37087     },
37088     
37089     closable : function(closable)
37090     {
37091         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37092             
37093             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37094             
37095             if(closable){
37096                 el.show();
37097                 return;
37098             }
37099             
37100             el.hide();
37101             
37102         }, this);
37103     },
37104     
37105     xhrOnLoad : function(xhr)
37106     {
37107         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37108             el.remove();
37109         }, this);
37110         
37111         if (xhr.readyState !== 4) {
37112             this.arrange();
37113             this.fireEvent('exception', this, xhr);
37114             return;
37115         }
37116
37117         var response = Roo.decode(xhr.responseText);
37118         
37119         if(!response.success){
37120             this.arrange();
37121             this.fireEvent('exception', this, xhr);
37122             return;
37123         }
37124         
37125         var file = this.renderPreview(response.data);
37126         
37127         this.files.push(file);
37128         
37129         this.arrange();
37130         
37131         this.fireEvent('afterupload', this, xhr);
37132         
37133     },
37134     
37135     xhrOnError : function(xhr)
37136     {
37137         Roo.log('xhr on error');
37138         
37139         var response = Roo.decode(xhr.responseText);
37140           
37141         Roo.log(response);
37142         
37143         this.arrange();
37144     },
37145     
37146     process : function(file)
37147     {
37148         if(this.fireEvent('process', this, file) !== false){
37149             if(this.editable && file.type.indexOf('image') != -1){
37150                 this.fireEvent('edit', this, file);
37151                 return;
37152             }
37153
37154             this.uploadStart(file, false);
37155
37156             return;
37157         }
37158         
37159     },
37160     
37161     uploadStart : function(file, crop)
37162     {
37163         this.xhr = new XMLHttpRequest();
37164         
37165         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37166             this.arrange();
37167             return;
37168         }
37169         
37170         file.xhr = this.xhr;
37171             
37172         this.managerEl.createChild({
37173             tag : 'div',
37174             cls : 'roo-document-manager-loading',
37175             cn : [
37176                 {
37177                     tag : 'div',
37178                     tooltip : file.name,
37179                     cls : 'roo-document-manager-thumb',
37180                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37181                 }
37182             ]
37183
37184         });
37185
37186         this.xhr.open(this.method, this.url, true);
37187         
37188         var headers = {
37189             "Accept": "application/json",
37190             "Cache-Control": "no-cache",
37191             "X-Requested-With": "XMLHttpRequest"
37192         };
37193         
37194         for (var headerName in headers) {
37195             var headerValue = headers[headerName];
37196             if (headerValue) {
37197                 this.xhr.setRequestHeader(headerName, headerValue);
37198             }
37199         }
37200         
37201         var _this = this;
37202         
37203         this.xhr.onload = function()
37204         {
37205             _this.xhrOnLoad(_this.xhr);
37206         }
37207         
37208         this.xhr.onerror = function()
37209         {
37210             _this.xhrOnError(_this.xhr);
37211         }
37212         
37213         var formData = new FormData();
37214
37215         formData.append('returnHTML', 'NO');
37216         
37217         if(crop){
37218             formData.append('crop', crop);
37219         }
37220         
37221         formData.append(this.paramName, file, file.name);
37222         
37223         var options = {
37224             file : file, 
37225             manually : false
37226         };
37227         
37228         if(this.fireEvent('prepare', this, formData, options) != false){
37229             
37230             if(options.manually){
37231                 return;
37232             }
37233             
37234             this.xhr.send(formData);
37235             return;
37236         };
37237         
37238         this.uploadCancel();
37239     },
37240     
37241     uploadCancel : function()
37242     {
37243         if (this.xhr) {
37244             this.xhr.abort();
37245         }
37246         
37247         this.delegates = [];
37248         
37249         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37250             el.remove();
37251         }, this);
37252         
37253         this.arrange();
37254     },
37255     
37256     renderPreview : function(file)
37257     {
37258         if(typeof(file.target) != 'undefined' && file.target){
37259             return file;
37260         }
37261         
37262         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
37263         
37264         var previewEl = this.managerEl.createChild({
37265             tag : 'div',
37266             cls : 'roo-document-manager-preview',
37267             cn : [
37268                 {
37269                     tag : 'div',
37270                     tooltip : file[this.toolTipName],
37271                     cls : 'roo-document-manager-thumb',
37272                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
37273                 },
37274                 {
37275                     tag : 'button',
37276                     cls : 'close',
37277                     html : '<i class="fa fa-times-circle"></i>'
37278                 }
37279             ]
37280         });
37281
37282         var close = previewEl.select('button.close', true).first();
37283
37284         close.on('click', this.onRemove, this, file);
37285
37286         file.target = previewEl;
37287
37288         var image = previewEl.select('img', true).first();
37289         
37290         var _this = this;
37291         
37292         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
37293         
37294         image.on('click', this.onClick, this, file);
37295         
37296         this.fireEvent('previewrendered', this, file);
37297         
37298         return file;
37299         
37300     },
37301     
37302     onPreviewLoad : function(file, image)
37303     {
37304         if(typeof(file.target) == 'undefined' || !file.target){
37305             return;
37306         }
37307         
37308         var width = image.dom.naturalWidth || image.dom.width;
37309         var height = image.dom.naturalHeight || image.dom.height;
37310         
37311         if(!this.previewResize) {
37312             return;
37313         }
37314         
37315         if(width > height){
37316             file.target.addClass('wide');
37317             return;
37318         }
37319         
37320         file.target.addClass('tall');
37321         return;
37322         
37323     },
37324     
37325     uploadFromSource : function(file, crop)
37326     {
37327         this.xhr = new XMLHttpRequest();
37328         
37329         this.managerEl.createChild({
37330             tag : 'div',
37331             cls : 'roo-document-manager-loading',
37332             cn : [
37333                 {
37334                     tag : 'div',
37335                     tooltip : file.name,
37336                     cls : 'roo-document-manager-thumb',
37337                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37338                 }
37339             ]
37340
37341         });
37342
37343         this.xhr.open(this.method, this.url, true);
37344         
37345         var headers = {
37346             "Accept": "application/json",
37347             "Cache-Control": "no-cache",
37348             "X-Requested-With": "XMLHttpRequest"
37349         };
37350         
37351         for (var headerName in headers) {
37352             var headerValue = headers[headerName];
37353             if (headerValue) {
37354                 this.xhr.setRequestHeader(headerName, headerValue);
37355             }
37356         }
37357         
37358         var _this = this;
37359         
37360         this.xhr.onload = function()
37361         {
37362             _this.xhrOnLoad(_this.xhr);
37363         }
37364         
37365         this.xhr.onerror = function()
37366         {
37367             _this.xhrOnError(_this.xhr);
37368         }
37369         
37370         var formData = new FormData();
37371
37372         formData.append('returnHTML', 'NO');
37373         
37374         formData.append('crop', crop);
37375         
37376         if(typeof(file.filename) != 'undefined'){
37377             formData.append('filename', file.filename);
37378         }
37379         
37380         if(typeof(file.mimetype) != 'undefined'){
37381             formData.append('mimetype', file.mimetype);
37382         }
37383         
37384         Roo.log(formData);
37385         
37386         if(this.fireEvent('prepare', this, formData) != false){
37387             this.xhr.send(formData);
37388         };
37389     }
37390 });
37391
37392 /*
37393 * Licence: LGPL
37394 */
37395
37396 /**
37397  * @class Roo.bootstrap.DocumentViewer
37398  * @extends Roo.bootstrap.Component
37399  * Bootstrap DocumentViewer class
37400  * @cfg {Boolean} showDownload (true|false) show download button (default true)
37401  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
37402  * 
37403  * @constructor
37404  * Create a new DocumentViewer
37405  * @param {Object} config The config object
37406  */
37407
37408 Roo.bootstrap.DocumentViewer = function(config){
37409     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
37410     
37411     this.addEvents({
37412         /**
37413          * @event initial
37414          * Fire after initEvent
37415          * @param {Roo.bootstrap.DocumentViewer} this
37416          */
37417         "initial" : true,
37418         /**
37419          * @event click
37420          * Fire after click
37421          * @param {Roo.bootstrap.DocumentViewer} this
37422          */
37423         "click" : true,
37424         /**
37425          * @event download
37426          * Fire after download button
37427          * @param {Roo.bootstrap.DocumentViewer} this
37428          */
37429         "download" : true,
37430         /**
37431          * @event trash
37432          * Fire after trash button
37433          * @param {Roo.bootstrap.DocumentViewer} this
37434          */
37435         "trash" : true
37436         
37437     });
37438 };
37439
37440 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
37441     
37442     showDownload : true,
37443     
37444     showTrash : true,
37445     
37446     getAutoCreate : function()
37447     {
37448         var cfg = {
37449             tag : 'div',
37450             cls : 'roo-document-viewer',
37451             cn : [
37452                 {
37453                     tag : 'div',
37454                     cls : 'roo-document-viewer-body',
37455                     cn : [
37456                         {
37457                             tag : 'div',
37458                             cls : 'roo-document-viewer-thumb',
37459                             cn : [
37460                                 {
37461                                     tag : 'img',
37462                                     cls : 'roo-document-viewer-image'
37463                                 }
37464                             ]
37465                         }
37466                     ]
37467                 },
37468                 {
37469                     tag : 'div',
37470                     cls : 'roo-document-viewer-footer',
37471                     cn : {
37472                         tag : 'div',
37473                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
37474                         cn : [
37475                             {
37476                                 tag : 'div',
37477                                 cls : 'btn-group roo-document-viewer-download',
37478                                 cn : [
37479                                     {
37480                                         tag : 'button',
37481                                         cls : 'btn btn-default',
37482                                         html : '<i class="fa fa-download"></i>'
37483                                     }
37484                                 ]
37485                             },
37486                             {
37487                                 tag : 'div',
37488                                 cls : 'btn-group roo-document-viewer-trash',
37489                                 cn : [
37490                                     {
37491                                         tag : 'button',
37492                                         cls : 'btn btn-default',
37493                                         html : '<i class="fa fa-trash"></i>'
37494                                     }
37495                                 ]
37496                             }
37497                         ]
37498                     }
37499                 }
37500             ]
37501         };
37502         
37503         return cfg;
37504     },
37505     
37506     initEvents : function()
37507     {
37508         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
37509         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37510         
37511         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
37512         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37513         
37514         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
37515         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37516         
37517         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
37518         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
37519         
37520         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
37521         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
37522         
37523         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
37524         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
37525         
37526         this.bodyEl.on('click', this.onClick, this);
37527         this.downloadBtn.on('click', this.onDownload, this);
37528         this.trashBtn.on('click', this.onTrash, this);
37529         
37530         this.downloadBtn.hide();
37531         this.trashBtn.hide();
37532         
37533         if(this.showDownload){
37534             this.downloadBtn.show();
37535         }
37536         
37537         if(this.showTrash){
37538             this.trashBtn.show();
37539         }
37540         
37541         if(!this.showDownload && !this.showTrash) {
37542             this.footerEl.hide();
37543         }
37544         
37545     },
37546     
37547     initial : function()
37548     {
37549         this.fireEvent('initial', this);
37550         
37551     },
37552     
37553     onClick : function(e)
37554     {
37555         e.preventDefault();
37556         
37557         this.fireEvent('click', this);
37558     },
37559     
37560     onDownload : function(e)
37561     {
37562         e.preventDefault();
37563         
37564         this.fireEvent('download', this);
37565     },
37566     
37567     onTrash : function(e)
37568     {
37569         e.preventDefault();
37570         
37571         this.fireEvent('trash', this);
37572     }
37573     
37574 });
37575 /*
37576  * - LGPL
37577  *
37578  * FieldLabel
37579  * 
37580  */
37581
37582 /**
37583  * @class Roo.bootstrap.form.FieldLabel
37584  * @extends Roo.bootstrap.Component
37585  * Bootstrap FieldLabel class
37586  * @cfg {String} html contents of the element
37587  * @cfg {String} tag tag of the element default label
37588  * @cfg {String} cls class of the element
37589  * @cfg {String} target label target 
37590  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
37591  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
37592  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
37593  * @cfg {String} iconTooltip default "This field is required"
37594  * @cfg {String} indicatorpos (left|right) default left
37595  * 
37596  * @constructor
37597  * Create a new FieldLabel
37598  * @param {Object} config The config object
37599  */
37600
37601 Roo.bootstrap.form.FieldLabel = function(config){
37602     Roo.bootstrap.Element.superclass.constructor.call(this, config);
37603     
37604     this.addEvents({
37605             /**
37606              * @event invalid
37607              * Fires after the field has been marked as invalid.
37608              * @param {Roo.form.FieldLabel} this
37609              * @param {String} msg The validation message
37610              */
37611             invalid : true,
37612             /**
37613              * @event valid
37614              * Fires after the field has been validated with no errors.
37615              * @param {Roo.form.FieldLabel} this
37616              */
37617             valid : true
37618         });
37619 };
37620
37621 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
37622     
37623     tag: 'label',
37624     cls: '',
37625     html: '',
37626     target: '',
37627     allowBlank : true,
37628     invalidClass : 'has-warning',
37629     validClass : 'has-success',
37630     iconTooltip : 'This field is required',
37631     indicatorpos : 'left',
37632     
37633     getAutoCreate : function(){
37634         
37635         var cls = "";
37636         if (!this.allowBlank) {
37637             cls  = "visible";
37638         }
37639         
37640         var cfg = {
37641             tag : this.tag,
37642             cls : 'roo-bootstrap-field-label ' + this.cls,
37643             for : this.target,
37644             cn : [
37645                 {
37646                     tag : 'i',
37647                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
37648                     tooltip : this.iconTooltip
37649                 },
37650                 {
37651                     tag : 'span',
37652                     html : this.html
37653                 }
37654             ] 
37655         };
37656         
37657         if(this.indicatorpos == 'right'){
37658             var cfg = {
37659                 tag : this.tag,
37660                 cls : 'roo-bootstrap-field-label ' + this.cls,
37661                 for : this.target,
37662                 cn : [
37663                     {
37664                         tag : 'span',
37665                         html : this.html
37666                     },
37667                     {
37668                         tag : 'i',
37669                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
37670                         tooltip : this.iconTooltip
37671                     }
37672                 ] 
37673             };
37674         }
37675         
37676         return cfg;
37677     },
37678     
37679     initEvents: function() 
37680     {
37681         Roo.bootstrap.Element.superclass.initEvents.call(this);
37682         
37683         this.indicator = this.indicatorEl();
37684         
37685         if(this.indicator){
37686             this.indicator.removeClass('visible');
37687             this.indicator.addClass('invisible');
37688         }
37689         
37690         Roo.bootstrap.form.FieldLabel.register(this);
37691     },
37692     
37693     indicatorEl : function()
37694     {
37695         var indicator = this.el.select('i.roo-required-indicator',true).first();
37696         
37697         if(!indicator){
37698             return false;
37699         }
37700         
37701         return indicator;
37702         
37703     },
37704     
37705     /**
37706      * Mark this field as valid
37707      */
37708     markValid : function()
37709     {
37710         if(this.indicator){
37711             this.indicator.removeClass('visible');
37712             this.indicator.addClass('invisible');
37713         }
37714         if (Roo.bootstrap.version == 3) {
37715             this.el.removeClass(this.invalidClass);
37716             this.el.addClass(this.validClass);
37717         } else {
37718             this.el.removeClass('is-invalid');
37719             this.el.addClass('is-valid');
37720         }
37721         
37722         
37723         this.fireEvent('valid', this);
37724     },
37725     
37726     /**
37727      * Mark this field as invalid
37728      * @param {String} msg The validation message
37729      */
37730     markInvalid : function(msg)
37731     {
37732         if(this.indicator){
37733             this.indicator.removeClass('invisible');
37734             this.indicator.addClass('visible');
37735         }
37736           if (Roo.bootstrap.version == 3) {
37737             this.el.removeClass(this.validClass);
37738             this.el.addClass(this.invalidClass);
37739         } else {
37740             this.el.removeClass('is-valid');
37741             this.el.addClass('is-invalid');
37742         }
37743         
37744         
37745         this.fireEvent('invalid', this, msg);
37746     }
37747     
37748    
37749 });
37750
37751 Roo.apply(Roo.bootstrap.form.FieldLabel, {
37752     
37753     groups: {},
37754     
37755      /**
37756     * register a FieldLabel Group
37757     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
37758     */
37759     register : function(label)
37760     {
37761         if(this.groups.hasOwnProperty(label.target)){
37762             return;
37763         }
37764      
37765         this.groups[label.target] = label;
37766         
37767     },
37768     /**
37769     * fetch a FieldLabel Group based on the target
37770     * @param {string} target
37771     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
37772     */
37773     get: function(target) {
37774         if (typeof(this.groups[target]) == 'undefined') {
37775             return false;
37776         }
37777         
37778         return this.groups[target] ;
37779     }
37780 });
37781
37782  
37783
37784  /*
37785  * - LGPL
37786  *
37787  * page DateSplitField.
37788  * 
37789  */
37790
37791
37792 /**
37793  * @class Roo.bootstrap.form.DateSplitField
37794  * @extends Roo.bootstrap.Component
37795  * Bootstrap DateSplitField class
37796  * @cfg {string} fieldLabel - the label associated
37797  * @cfg {Number} labelWidth set the width of label (0-12)
37798  * @cfg {String} labelAlign (top|left)
37799  * @cfg {Boolean} dayAllowBlank (true|false) default false
37800  * @cfg {Boolean} monthAllowBlank (true|false) default false
37801  * @cfg {Boolean} yearAllowBlank (true|false) default false
37802  * @cfg {string} dayPlaceholder 
37803  * @cfg {string} monthPlaceholder
37804  * @cfg {string} yearPlaceholder
37805  * @cfg {string} dayFormat default 'd'
37806  * @cfg {string} monthFormat default 'm'
37807  * @cfg {string} yearFormat default 'Y'
37808  * @cfg {Number} labellg set the width of label (1-12)
37809  * @cfg {Number} labelmd set the width of label (1-12)
37810  * @cfg {Number} labelsm set the width of label (1-12)
37811  * @cfg {Number} labelxs set the width of label (1-12)
37812
37813  *     
37814  * @constructor
37815  * Create a new DateSplitField
37816  * @param {Object} config The config object
37817  */
37818
37819 Roo.bootstrap.form.DateSplitField = function(config){
37820     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
37821     
37822     this.addEvents({
37823         // raw events
37824          /**
37825          * @event years
37826          * getting the data of years
37827          * @param {Roo.bootstrap.form.DateSplitField} this
37828          * @param {Object} years
37829          */
37830         "years" : true,
37831         /**
37832          * @event days
37833          * getting the data of days
37834          * @param {Roo.bootstrap.form.DateSplitField} this
37835          * @param {Object} days
37836          */
37837         "days" : true,
37838         /**
37839          * @event invalid
37840          * Fires after the field has been marked as invalid.
37841          * @param {Roo.form.Field} this
37842          * @param {String} msg The validation message
37843          */
37844         invalid : true,
37845        /**
37846          * @event valid
37847          * Fires after the field has been validated with no errors.
37848          * @param {Roo.form.Field} this
37849          */
37850         valid : true
37851     });
37852 };
37853
37854 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
37855     
37856     fieldLabel : '',
37857     labelAlign : 'top',
37858     labelWidth : 3,
37859     dayAllowBlank : false,
37860     monthAllowBlank : false,
37861     yearAllowBlank : false,
37862     dayPlaceholder : '',
37863     monthPlaceholder : '',
37864     yearPlaceholder : '',
37865     dayFormat : 'd',
37866     monthFormat : 'm',
37867     yearFormat : 'Y',
37868     isFormField : true,
37869     labellg : 0,
37870     labelmd : 0,
37871     labelsm : 0,
37872     labelxs : 0,
37873     
37874     getAutoCreate : function()
37875     {
37876         var cfg = {
37877             tag : 'div',
37878             cls : 'row roo-date-split-field-group',
37879             cn : [
37880                 {
37881                     tag : 'input',
37882                     type : 'hidden',
37883                     cls : 'form-hidden-field roo-date-split-field-group-value',
37884                     name : this.name
37885                 }
37886             ]
37887         };
37888         
37889         var labelCls = 'col-md-12';
37890         var contentCls = 'col-md-4';
37891         
37892         if(this.fieldLabel){
37893             
37894             var label = {
37895                 tag : 'div',
37896                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
37897                 cn : [
37898                     {
37899                         tag : 'label',
37900                         html : this.fieldLabel
37901                     }
37902                 ]
37903             };
37904             
37905             if(this.labelAlign == 'left'){
37906             
37907                 if(this.labelWidth > 12){
37908                     label.style = "width: " + this.labelWidth + 'px';
37909                 }
37910
37911                 if(this.labelWidth < 13 && this.labelmd == 0){
37912                     this.labelmd = this.labelWidth;
37913                 }
37914
37915                 if(this.labellg > 0){
37916                     labelCls = ' col-lg-' + this.labellg;
37917                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
37918                 }
37919
37920                 if(this.labelmd > 0){
37921                     labelCls = ' col-md-' + this.labelmd;
37922                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
37923                 }
37924
37925                 if(this.labelsm > 0){
37926                     labelCls = ' col-sm-' + this.labelsm;
37927                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
37928                 }
37929
37930                 if(this.labelxs > 0){
37931                     labelCls = ' col-xs-' + this.labelxs;
37932                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
37933                 }
37934             }
37935             
37936             label.cls += ' ' + labelCls;
37937             
37938             cfg.cn.push(label);
37939         }
37940         
37941         Roo.each(['day', 'month', 'year'], function(t){
37942             cfg.cn.push({
37943                 tag : 'div',
37944                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
37945             });
37946         }, this);
37947         
37948         return cfg;
37949     },
37950     
37951     inputEl: function ()
37952     {
37953         return this.el.select('.roo-date-split-field-group-value', true).first();
37954     },
37955     
37956     onRender : function(ct, position) 
37957     {
37958         var _this = this;
37959         
37960         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
37961         
37962         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
37963         
37964         this.dayField = new Roo.bootstrap.form.ComboBox({
37965             allowBlank : this.dayAllowBlank,
37966             alwaysQuery : true,
37967             displayField : 'value',
37968             editable : false,
37969             fieldLabel : '',
37970             forceSelection : true,
37971             mode : 'local',
37972             placeholder : this.dayPlaceholder,
37973             selectOnFocus : true,
37974             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
37975             triggerAction : 'all',
37976             typeAhead : true,
37977             valueField : 'value',
37978             store : new Roo.data.SimpleStore({
37979                 data : (function() {    
37980                     var days = [];
37981                     _this.fireEvent('days', _this, days);
37982                     return days;
37983                 })(),
37984                 fields : [ 'value' ]
37985             }),
37986             listeners : {
37987                 select : function (_self, record, index)
37988                 {
37989                     _this.setValue(_this.getValue());
37990                 }
37991             }
37992         });
37993
37994         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
37995         
37996         this.monthField = new Roo.bootstrap.form.MonthField({
37997             after : '<i class=\"fa fa-calendar\"></i>',
37998             allowBlank : this.monthAllowBlank,
37999             placeholder : this.monthPlaceholder,
38000             readOnly : true,
38001             listeners : {
38002                 render : function (_self)
38003                 {
38004                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
38005                         e.preventDefault();
38006                         _self.focus();
38007                     });
38008                 },
38009                 select : function (_self, oldvalue, newvalue)
38010                 {
38011                     _this.setValue(_this.getValue());
38012                 }
38013             }
38014         });
38015         
38016         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38017         
38018         this.yearField = new Roo.bootstrap.form.ComboBox({
38019             allowBlank : this.yearAllowBlank,
38020             alwaysQuery : true,
38021             displayField : 'value',
38022             editable : false,
38023             fieldLabel : '',
38024             forceSelection : true,
38025             mode : 'local',
38026             placeholder : this.yearPlaceholder,
38027             selectOnFocus : true,
38028             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38029             triggerAction : 'all',
38030             typeAhead : true,
38031             valueField : 'value',
38032             store : new Roo.data.SimpleStore({
38033                 data : (function() {
38034                     var years = [];
38035                     _this.fireEvent('years', _this, years);
38036                     return years;
38037                 })(),
38038                 fields : [ 'value' ]
38039             }),
38040             listeners : {
38041                 select : function (_self, record, index)
38042                 {
38043                     _this.setValue(_this.getValue());
38044                 }
38045             }
38046         });
38047
38048         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38049     },
38050     
38051     setValue : function(v, format)
38052     {
38053         this.inputEl.dom.value = v;
38054         
38055         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38056         
38057         var d = Date.parseDate(v, f);
38058         
38059         if(!d){
38060             this.validate();
38061             return;
38062         }
38063         
38064         this.setDay(d.format(this.dayFormat));
38065         this.setMonth(d.format(this.monthFormat));
38066         this.setYear(d.format(this.yearFormat));
38067         
38068         this.validate();
38069         
38070         return;
38071     },
38072     
38073     setDay : function(v)
38074     {
38075         this.dayField.setValue(v);
38076         this.inputEl.dom.value = this.getValue();
38077         this.validate();
38078         return;
38079     },
38080     
38081     setMonth : function(v)
38082     {
38083         this.monthField.setValue(v, true);
38084         this.inputEl.dom.value = this.getValue();
38085         this.validate();
38086         return;
38087     },
38088     
38089     setYear : function(v)
38090     {
38091         this.yearField.setValue(v);
38092         this.inputEl.dom.value = this.getValue();
38093         this.validate();
38094         return;
38095     },
38096     
38097     getDay : function()
38098     {
38099         return this.dayField.getValue();
38100     },
38101     
38102     getMonth : function()
38103     {
38104         return this.monthField.getValue();
38105     },
38106     
38107     getYear : function()
38108     {
38109         return this.yearField.getValue();
38110     },
38111     
38112     getValue : function()
38113     {
38114         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38115         
38116         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38117         
38118         return date;
38119     },
38120     
38121     reset : function()
38122     {
38123         this.setDay('');
38124         this.setMonth('');
38125         this.setYear('');
38126         this.inputEl.dom.value = '';
38127         this.validate();
38128         return;
38129     },
38130     
38131     validate : function()
38132     {
38133         var d = this.dayField.validate();
38134         var m = this.monthField.validate();
38135         var y = this.yearField.validate();
38136         
38137         var valid = true;
38138         
38139         if(
38140                 (!this.dayAllowBlank && !d) ||
38141                 (!this.monthAllowBlank && !m) ||
38142                 (!this.yearAllowBlank && !y)
38143         ){
38144             valid = false;
38145         }
38146         
38147         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38148             return valid;
38149         }
38150         
38151         if(valid){
38152             this.markValid();
38153             return valid;
38154         }
38155         
38156         this.markInvalid();
38157         
38158         return valid;
38159     },
38160     
38161     markValid : function()
38162     {
38163         
38164         var label = this.el.select('label', true).first();
38165         var icon = this.el.select('i.fa-star', true).first();
38166
38167         if(label && icon){
38168             icon.remove();
38169         }
38170         
38171         this.fireEvent('valid', this);
38172     },
38173     
38174      /**
38175      * Mark this field as invalid
38176      * @param {String} msg The validation message
38177      */
38178     markInvalid : function(msg)
38179     {
38180         
38181         var label = this.el.select('label', true).first();
38182         var icon = this.el.select('i.fa-star', true).first();
38183
38184         if(label && !icon){
38185             this.el.select('.roo-date-split-field-label', true).createChild({
38186                 tag : 'i',
38187                 cls : 'text-danger fa fa-lg fa-star',
38188                 tooltip : 'This field is required',
38189                 style : 'margin-right:5px;'
38190             }, label, true);
38191         }
38192         
38193         this.fireEvent('invalid', this, msg);
38194     },
38195     
38196     clearInvalid : function()
38197     {
38198         var label = this.el.select('label', true).first();
38199         var icon = this.el.select('i.fa-star', true).first();
38200
38201         if(label && icon){
38202             icon.remove();
38203         }
38204         
38205         this.fireEvent('valid', this);
38206     },
38207     
38208     getName: function()
38209     {
38210         return this.name;
38211     }
38212     
38213 });
38214
38215  
38216
38217 /**
38218  * @class Roo.bootstrap.LayoutMasonry
38219  * @extends Roo.bootstrap.Component
38220  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
38221  * Bootstrap Layout Masonry class
38222  *
38223  * This is based on 
38224  * http://masonry.desandro.com
38225  *
38226  * The idea is to render all the bricks based on vertical width...
38227  *
38228  * The original code extends 'outlayer' - we might need to use that....
38229
38230  * @constructor
38231  * Create a new Element
38232  * @param {Object} config The config object
38233  */
38234
38235 Roo.bootstrap.LayoutMasonry = function(config){
38236     
38237     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
38238     
38239     this.bricks = [];
38240     
38241     Roo.bootstrap.LayoutMasonry.register(this);
38242     
38243     this.addEvents({
38244         // raw events
38245         /**
38246          * @event layout
38247          * Fire after layout the items
38248          * @param {Roo.bootstrap.LayoutMasonry} this
38249          * @param {Roo.EventObject} e
38250          */
38251         "layout" : true
38252     });
38253     
38254 };
38255
38256 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
38257     
38258     /**
38259      * @cfg {Boolean} isLayoutInstant = no animation?
38260      */   
38261     isLayoutInstant : false, // needed?
38262    
38263     /**
38264      * @cfg {Number} boxWidth  width of the columns
38265      */   
38266     boxWidth : 450,
38267     
38268       /**
38269      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
38270      */   
38271     boxHeight : 0,
38272     
38273     /**
38274      * @cfg {Number} padWidth padding below box..
38275      */   
38276     padWidth : 10, 
38277     
38278     /**
38279      * @cfg {Number} gutter gutter width..
38280      */   
38281     gutter : 10,
38282     
38283      /**
38284      * @cfg {Number} maxCols maximum number of columns
38285      */   
38286     
38287     maxCols: 0,
38288     
38289     /**
38290      * @cfg {Boolean} isAutoInitial defalut true
38291      */   
38292     isAutoInitial : true, 
38293     
38294     containerWidth: 0,
38295     
38296     /**
38297      * @cfg {Boolean} isHorizontal defalut false
38298      */   
38299     isHorizontal : false, 
38300
38301     currentSize : null,
38302     
38303     tag: 'div',
38304     
38305     cls: '',
38306     
38307     bricks: null, //CompositeElement
38308     
38309     cols : 1,
38310     
38311     _isLayoutInited : false,
38312     
38313 //    isAlternative : false, // only use for vertical layout...
38314     
38315     /**
38316      * @cfg {Number} alternativePadWidth padding below box..
38317      */   
38318     alternativePadWidth : 50,
38319     
38320     selectedBrick : [],
38321     
38322     getAutoCreate : function(){
38323         
38324         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
38325         
38326         var cfg = {
38327             tag: this.tag,
38328             cls: 'blog-masonary-wrapper ' + this.cls,
38329             cn : {
38330                 cls : 'mas-boxes masonary'
38331             }
38332         };
38333         
38334         return cfg;
38335     },
38336     
38337     getChildContainer: function( )
38338     {
38339         if (this.boxesEl) {
38340             return this.boxesEl;
38341         }
38342         
38343         this.boxesEl = this.el.select('.mas-boxes').first();
38344         
38345         return this.boxesEl;
38346     },
38347     
38348     
38349     initEvents : function()
38350     {
38351         var _this = this;
38352         
38353         if(this.isAutoInitial){
38354             Roo.log('hook children rendered');
38355             this.on('childrenrendered', function() {
38356                 Roo.log('children rendered');
38357                 _this.initial();
38358             } ,this);
38359         }
38360     },
38361     
38362     initial : function()
38363     {
38364         this.selectedBrick = [];
38365         
38366         this.currentSize = this.el.getBox(true);
38367         
38368         Roo.EventManager.onWindowResize(this.resize, this); 
38369
38370         if(!this.isAutoInitial){
38371             this.layout();
38372             return;
38373         }
38374         
38375         this.layout();
38376         
38377         return;
38378         //this.layout.defer(500,this);
38379         
38380     },
38381     
38382     resize : function()
38383     {
38384         var cs = this.el.getBox(true);
38385         
38386         if (
38387                 this.currentSize.width == cs.width && 
38388                 this.currentSize.x == cs.x && 
38389                 this.currentSize.height == cs.height && 
38390                 this.currentSize.y == cs.y 
38391         ) {
38392             Roo.log("no change in with or X or Y");
38393             return;
38394         }
38395         
38396         this.currentSize = cs;
38397         
38398         this.layout();
38399         
38400     },
38401     
38402     layout : function()
38403     {   
38404         this._resetLayout();
38405         
38406         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38407         
38408         this.layoutItems( isInstant );
38409       
38410         this._isLayoutInited = true;
38411         
38412         this.fireEvent('layout', this);
38413         
38414     },
38415     
38416     _resetLayout : function()
38417     {
38418         if(this.isHorizontal){
38419             this.horizontalMeasureColumns();
38420             return;
38421         }
38422         
38423         this.verticalMeasureColumns();
38424         
38425     },
38426     
38427     verticalMeasureColumns : function()
38428     {
38429         this.getContainerWidth();
38430         
38431 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38432 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
38433 //            return;
38434 //        }
38435         
38436         var boxWidth = this.boxWidth + this.padWidth;
38437         
38438         if(this.containerWidth < this.boxWidth){
38439             boxWidth = this.containerWidth
38440         }
38441         
38442         var containerWidth = this.containerWidth;
38443         
38444         var cols = Math.floor(containerWidth / boxWidth);
38445         
38446         this.cols = Math.max( cols, 1 );
38447         
38448         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38449         
38450         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
38451         
38452         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
38453         
38454         this.colWidth = boxWidth + avail - this.padWidth;
38455         
38456         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
38457         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
38458     },
38459     
38460     horizontalMeasureColumns : function()
38461     {
38462         this.getContainerWidth();
38463         
38464         var boxWidth = this.boxWidth;
38465         
38466         if(this.containerWidth < boxWidth){
38467             boxWidth = this.containerWidth;
38468         }
38469         
38470         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
38471         
38472         this.el.setHeight(boxWidth);
38473         
38474     },
38475     
38476     getContainerWidth : function()
38477     {
38478         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38479     },
38480     
38481     layoutItems : function( isInstant )
38482     {
38483         Roo.log(this.bricks);
38484         
38485         var items = Roo.apply([], this.bricks);
38486         
38487         if(this.isHorizontal){
38488             this._horizontalLayoutItems( items , isInstant );
38489             return;
38490         }
38491         
38492 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38493 //            this._verticalAlternativeLayoutItems( items , isInstant );
38494 //            return;
38495 //        }
38496         
38497         this._verticalLayoutItems( items , isInstant );
38498         
38499     },
38500     
38501     _verticalLayoutItems : function ( items , isInstant)
38502     {
38503         if ( !items || !items.length ) {
38504             return;
38505         }
38506         
38507         var standard = [
38508             ['xs', 'xs', 'xs', 'tall'],
38509             ['xs', 'xs', 'tall'],
38510             ['xs', 'xs', 'sm'],
38511             ['xs', 'xs', 'xs'],
38512             ['xs', 'tall'],
38513             ['xs', 'sm'],
38514             ['xs', 'xs'],
38515             ['xs'],
38516             
38517             ['sm', 'xs', 'xs'],
38518             ['sm', 'xs'],
38519             ['sm'],
38520             
38521             ['tall', 'xs', 'xs', 'xs'],
38522             ['tall', 'xs', 'xs'],
38523             ['tall', 'xs'],
38524             ['tall']
38525             
38526         ];
38527         
38528         var queue = [];
38529         
38530         var boxes = [];
38531         
38532         var box = [];
38533         
38534         Roo.each(items, function(item, k){
38535             
38536             switch (item.size) {
38537                 // these layouts take up a full box,
38538                 case 'md' :
38539                 case 'md-left' :
38540                 case 'md-right' :
38541                 case 'wide' :
38542                     
38543                     if(box.length){
38544                         boxes.push(box);
38545                         box = [];
38546                     }
38547                     
38548                     boxes.push([item]);
38549                     
38550                     break;
38551                     
38552                 case 'xs' :
38553                 case 'sm' :
38554                 case 'tall' :
38555                     
38556                     box.push(item);
38557                     
38558                     break;
38559                 default :
38560                     break;
38561                     
38562             }
38563             
38564         }, this);
38565         
38566         if(box.length){
38567             boxes.push(box);
38568             box = [];
38569         }
38570         
38571         var filterPattern = function(box, length)
38572         {
38573             if(!box.length){
38574                 return;
38575             }
38576             
38577             var match = false;
38578             
38579             var pattern = box.slice(0, length);
38580             
38581             var format = [];
38582             
38583             Roo.each(pattern, function(i){
38584                 format.push(i.size);
38585             }, this);
38586             
38587             Roo.each(standard, function(s){
38588                 
38589                 if(String(s) != String(format)){
38590                     return;
38591                 }
38592                 
38593                 match = true;
38594                 return false;
38595                 
38596             }, this);
38597             
38598             if(!match && length == 1){
38599                 return;
38600             }
38601             
38602             if(!match){
38603                 filterPattern(box, length - 1);
38604                 return;
38605             }
38606                 
38607             queue.push(pattern);
38608
38609             box = box.slice(length, box.length);
38610
38611             filterPattern(box, 4);
38612
38613             return;
38614             
38615         }
38616         
38617         Roo.each(boxes, function(box, k){
38618             
38619             if(!box.length){
38620                 return;
38621             }
38622             
38623             if(box.length == 1){
38624                 queue.push(box);
38625                 return;
38626             }
38627             
38628             filterPattern(box, 4);
38629             
38630         }, this);
38631         
38632         this._processVerticalLayoutQueue( queue, isInstant );
38633         
38634     },
38635     
38636 //    _verticalAlternativeLayoutItems : function( items , isInstant )
38637 //    {
38638 //        if ( !items || !items.length ) {
38639 //            return;
38640 //        }
38641 //
38642 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
38643 //        
38644 //    },
38645     
38646     _horizontalLayoutItems : function ( items , isInstant)
38647     {
38648         if ( !items || !items.length || items.length < 3) {
38649             return;
38650         }
38651         
38652         items.reverse();
38653         
38654         var eItems = items.slice(0, 3);
38655         
38656         items = items.slice(3, items.length);
38657         
38658         var standard = [
38659             ['xs', 'xs', 'xs', 'wide'],
38660             ['xs', 'xs', 'wide'],
38661             ['xs', 'xs', 'sm'],
38662             ['xs', 'xs', 'xs'],
38663             ['xs', 'wide'],
38664             ['xs', 'sm'],
38665             ['xs', 'xs'],
38666             ['xs'],
38667             
38668             ['sm', 'xs', 'xs'],
38669             ['sm', 'xs'],
38670             ['sm'],
38671             
38672             ['wide', 'xs', 'xs', 'xs'],
38673             ['wide', 'xs', 'xs'],
38674             ['wide', 'xs'],
38675             ['wide'],
38676             
38677             ['wide-thin']
38678         ];
38679         
38680         var queue = [];
38681         
38682         var boxes = [];
38683         
38684         var box = [];
38685         
38686         Roo.each(items, function(item, k){
38687             
38688             switch (item.size) {
38689                 case 'md' :
38690                 case 'md-left' :
38691                 case 'md-right' :
38692                 case 'tall' :
38693                     
38694                     if(box.length){
38695                         boxes.push(box);
38696                         box = [];
38697                     }
38698                     
38699                     boxes.push([item]);
38700                     
38701                     break;
38702                     
38703                 case 'xs' :
38704                 case 'sm' :
38705                 case 'wide' :
38706                 case 'wide-thin' :
38707                     
38708                     box.push(item);
38709                     
38710                     break;
38711                 default :
38712                     break;
38713                     
38714             }
38715             
38716         }, this);
38717         
38718         if(box.length){
38719             boxes.push(box);
38720             box = [];
38721         }
38722         
38723         var filterPattern = function(box, length)
38724         {
38725             if(!box.length){
38726                 return;
38727             }
38728             
38729             var match = false;
38730             
38731             var pattern = box.slice(0, length);
38732             
38733             var format = [];
38734             
38735             Roo.each(pattern, function(i){
38736                 format.push(i.size);
38737             }, this);
38738             
38739             Roo.each(standard, function(s){
38740                 
38741                 if(String(s) != String(format)){
38742                     return;
38743                 }
38744                 
38745                 match = true;
38746                 return false;
38747                 
38748             }, this);
38749             
38750             if(!match && length == 1){
38751                 return;
38752             }
38753             
38754             if(!match){
38755                 filterPattern(box, length - 1);
38756                 return;
38757             }
38758                 
38759             queue.push(pattern);
38760
38761             box = box.slice(length, box.length);
38762
38763             filterPattern(box, 4);
38764
38765             return;
38766             
38767         }
38768         
38769         Roo.each(boxes, function(box, k){
38770             
38771             if(!box.length){
38772                 return;
38773             }
38774             
38775             if(box.length == 1){
38776                 queue.push(box);
38777                 return;
38778             }
38779             
38780             filterPattern(box, 4);
38781             
38782         }, this);
38783         
38784         
38785         var prune = [];
38786         
38787         var pos = this.el.getBox(true);
38788         
38789         var minX = pos.x;
38790         
38791         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38792         
38793         var hit_end = false;
38794         
38795         Roo.each(queue, function(box){
38796             
38797             if(hit_end){
38798                 
38799                 Roo.each(box, function(b){
38800                 
38801                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
38802                     b.el.hide();
38803
38804                 }, this);
38805
38806                 return;
38807             }
38808             
38809             var mx = 0;
38810             
38811             Roo.each(box, function(b){
38812                 
38813                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
38814                 b.el.show();
38815
38816                 mx = Math.max(mx, b.x);
38817                 
38818             }, this);
38819             
38820             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
38821             
38822             if(maxX < minX){
38823                 
38824                 Roo.each(box, function(b){
38825                 
38826                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
38827                     b.el.hide();
38828                     
38829                 }, this);
38830                 
38831                 hit_end = true;
38832                 
38833                 return;
38834             }
38835             
38836             prune.push(box);
38837             
38838         }, this);
38839         
38840         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
38841     },
38842     
38843     /** Sets position of item in DOM
38844     * @param {Element} item
38845     * @param {Number} x - horizontal position
38846     * @param {Number} y - vertical position
38847     * @param {Boolean} isInstant - disables transitions
38848     */
38849     _processVerticalLayoutQueue : function( queue, isInstant )
38850     {
38851         var pos = this.el.getBox(true);
38852         var x = pos.x;
38853         var y = pos.y;
38854         var maxY = [];
38855         
38856         for (var i = 0; i < this.cols; i++){
38857             maxY[i] = pos.y;
38858         }
38859         
38860         Roo.each(queue, function(box, k){
38861             
38862             var col = k % this.cols;
38863             
38864             Roo.each(box, function(b,kk){
38865                 
38866                 b.el.position('absolute');
38867                 
38868                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38869                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
38870                 
38871                 if(b.size == 'md-left' || b.size == 'md-right'){
38872                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
38873                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
38874                 }
38875                 
38876                 b.el.setWidth(width);
38877                 b.el.setHeight(height);
38878                 // iframe?
38879                 b.el.select('iframe',true).setSize(width,height);
38880                 
38881             }, this);
38882             
38883             for (var i = 0; i < this.cols; i++){
38884                 
38885                 if(maxY[i] < maxY[col]){
38886                     col = i;
38887                     continue;
38888                 }
38889                 
38890                 col = Math.min(col, i);
38891                 
38892             }
38893             
38894             x = pos.x + col * (this.colWidth + this.padWidth);
38895             
38896             y = maxY[col];
38897             
38898             var positions = [];
38899             
38900             switch (box.length){
38901                 case 1 :
38902                     positions = this.getVerticalOneBoxColPositions(x, y, box);
38903                     break;
38904                 case 2 :
38905                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
38906                     break;
38907                 case 3 :
38908                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
38909                     break;
38910                 case 4 :
38911                     positions = this.getVerticalFourBoxColPositions(x, y, box);
38912                     break;
38913                 default :
38914                     break;
38915             }
38916             
38917             Roo.each(box, function(b,kk){
38918                 
38919                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
38920                 
38921                 var sz = b.el.getSize();
38922                 
38923                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
38924                 
38925             }, this);
38926             
38927         }, this);
38928         
38929         var mY = 0;
38930         
38931         for (var i = 0; i < this.cols; i++){
38932             mY = Math.max(mY, maxY[i]);
38933         }
38934         
38935         this.el.setHeight(mY - pos.y);
38936         
38937     },
38938     
38939 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
38940 //    {
38941 //        var pos = this.el.getBox(true);
38942 //        var x = pos.x;
38943 //        var y = pos.y;
38944 //        var maxX = pos.right;
38945 //        
38946 //        var maxHeight = 0;
38947 //        
38948 //        Roo.each(items, function(item, k){
38949 //            
38950 //            var c = k % 2;
38951 //            
38952 //            item.el.position('absolute');
38953 //                
38954 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
38955 //
38956 //            item.el.setWidth(width);
38957 //
38958 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
38959 //
38960 //            item.el.setHeight(height);
38961 //            
38962 //            if(c == 0){
38963 //                item.el.setXY([x, y], isInstant ? false : true);
38964 //            } else {
38965 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
38966 //            }
38967 //            
38968 //            y = y + height + this.alternativePadWidth;
38969 //            
38970 //            maxHeight = maxHeight + height + this.alternativePadWidth;
38971 //            
38972 //        }, this);
38973 //        
38974 //        this.el.setHeight(maxHeight);
38975 //        
38976 //    },
38977     
38978     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
38979     {
38980         var pos = this.el.getBox(true);
38981         
38982         var minX = pos.x;
38983         var minY = pos.y;
38984         
38985         var maxX = pos.right;
38986         
38987         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
38988         
38989         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38990         
38991         Roo.each(queue, function(box, k){
38992             
38993             Roo.each(box, function(b, kk){
38994                 
38995                 b.el.position('absolute');
38996                 
38997                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38998                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
38999                 
39000                 if(b.size == 'md-left' || b.size == 'md-right'){
39001                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39002                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39003                 }
39004                 
39005                 b.el.setWidth(width);
39006                 b.el.setHeight(height);
39007                 
39008             }, this);
39009             
39010             if(!box.length){
39011                 return;
39012             }
39013             
39014             var positions = [];
39015             
39016             switch (box.length){
39017                 case 1 :
39018                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39019                     break;
39020                 case 2 :
39021                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39022                     break;
39023                 case 3 :
39024                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39025                     break;
39026                 case 4 :
39027                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39028                     break;
39029                 default :
39030                     break;
39031             }
39032             
39033             Roo.each(box, function(b,kk){
39034                 
39035                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39036                 
39037                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39038                 
39039             }, this);
39040             
39041         }, this);
39042         
39043     },
39044     
39045     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39046     {
39047         Roo.each(eItems, function(b,k){
39048             
39049             b.size = (k == 0) ? 'sm' : 'xs';
39050             b.x = (k == 0) ? 2 : 1;
39051             b.y = (k == 0) ? 2 : 1;
39052             
39053             b.el.position('absolute');
39054             
39055             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39056                 
39057             b.el.setWidth(width);
39058             
39059             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39060             
39061             b.el.setHeight(height);
39062             
39063         }, this);
39064
39065         var positions = [];
39066         
39067         positions.push({
39068             x : maxX - this.unitWidth * 2 - this.gutter,
39069             y : minY
39070         });
39071         
39072         positions.push({
39073             x : maxX - this.unitWidth,
39074             y : minY + (this.unitWidth + this.gutter) * 2
39075         });
39076         
39077         positions.push({
39078             x : maxX - this.unitWidth * 3 - this.gutter * 2,
39079             y : minY
39080         });
39081         
39082         Roo.each(eItems, function(b,k){
39083             
39084             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39085
39086         }, this);
39087         
39088     },
39089     
39090     getVerticalOneBoxColPositions : function(x, y, box)
39091     {
39092         var pos = [];
39093         
39094         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39095         
39096         if(box[0].size == 'md-left'){
39097             rand = 0;
39098         }
39099         
39100         if(box[0].size == 'md-right'){
39101             rand = 1;
39102         }
39103         
39104         pos.push({
39105             x : x + (this.unitWidth + this.gutter) * rand,
39106             y : y
39107         });
39108         
39109         return pos;
39110     },
39111     
39112     getVerticalTwoBoxColPositions : function(x, y, box)
39113     {
39114         var pos = [];
39115         
39116         if(box[0].size == 'xs'){
39117             
39118             pos.push({
39119                 x : x,
39120                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39121             });
39122
39123             pos.push({
39124                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39125                 y : y
39126             });
39127             
39128             return pos;
39129             
39130         }
39131         
39132         pos.push({
39133             x : x,
39134             y : y
39135         });
39136
39137         pos.push({
39138             x : x + (this.unitWidth + this.gutter) * 2,
39139             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39140         });
39141         
39142         return pos;
39143         
39144     },
39145     
39146     getVerticalThreeBoxColPositions : function(x, y, box)
39147     {
39148         var pos = [];
39149         
39150         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39151             
39152             pos.push({
39153                 x : x,
39154                 y : y
39155             });
39156
39157             pos.push({
39158                 x : x + (this.unitWidth + this.gutter) * 1,
39159                 y : y
39160             });
39161             
39162             pos.push({
39163                 x : x + (this.unitWidth + this.gutter) * 2,
39164                 y : y
39165             });
39166             
39167             return pos;
39168             
39169         }
39170         
39171         if(box[0].size == 'xs' && box[1].size == 'xs'){
39172             
39173             pos.push({
39174                 x : x,
39175                 y : y
39176             });
39177
39178             pos.push({
39179                 x : x,
39180                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
39181             });
39182             
39183             pos.push({
39184                 x : x + (this.unitWidth + this.gutter) * 1,
39185                 y : y
39186             });
39187             
39188             return pos;
39189             
39190         }
39191         
39192         pos.push({
39193             x : x,
39194             y : y
39195         });
39196
39197         pos.push({
39198             x : x + (this.unitWidth + this.gutter) * 2,
39199             y : y
39200         });
39201
39202         pos.push({
39203             x : x + (this.unitWidth + this.gutter) * 2,
39204             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
39205         });
39206             
39207         return pos;
39208         
39209     },
39210     
39211     getVerticalFourBoxColPositions : function(x, y, box)
39212     {
39213         var pos = [];
39214         
39215         if(box[0].size == 'xs'){
39216             
39217             pos.push({
39218                 x : x,
39219                 y : y
39220             });
39221
39222             pos.push({
39223                 x : x,
39224                 y : y + (this.unitHeight + this.gutter) * 1
39225             });
39226             
39227             pos.push({
39228                 x : x,
39229                 y : y + (this.unitHeight + this.gutter) * 2
39230             });
39231             
39232             pos.push({
39233                 x : x + (this.unitWidth + this.gutter) * 1,
39234                 y : y
39235             });
39236             
39237             return pos;
39238             
39239         }
39240         
39241         pos.push({
39242             x : x,
39243             y : y
39244         });
39245
39246         pos.push({
39247             x : x + (this.unitWidth + this.gutter) * 2,
39248             y : y
39249         });
39250
39251         pos.push({
39252             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
39253             y : y + (this.unitHeight + this.gutter) * 1
39254         });
39255
39256         pos.push({
39257             x : x + (this.unitWidth + this.gutter) * 2,
39258             y : y + (this.unitWidth + this.gutter) * 2
39259         });
39260
39261         return pos;
39262         
39263     },
39264     
39265     getHorizontalOneBoxColPositions : function(maxX, minY, box)
39266     {
39267         var pos = [];
39268         
39269         if(box[0].size == 'md-left'){
39270             pos.push({
39271                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39272                 y : minY
39273             });
39274             
39275             return pos;
39276         }
39277         
39278         if(box[0].size == 'md-right'){
39279             pos.push({
39280                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39281                 y : minY + (this.unitWidth + this.gutter) * 1
39282             });
39283             
39284             return pos;
39285         }
39286         
39287         var rand = Math.floor(Math.random() * (4 - box[0].y));
39288         
39289         pos.push({
39290             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39291             y : minY + (this.unitWidth + this.gutter) * rand
39292         });
39293         
39294         return pos;
39295         
39296     },
39297     
39298     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
39299     {
39300         var pos = [];
39301         
39302         if(box[0].size == 'xs'){
39303             
39304             pos.push({
39305                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39306                 y : minY
39307             });
39308
39309             pos.push({
39310                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39311                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
39312             });
39313             
39314             return pos;
39315             
39316         }
39317         
39318         pos.push({
39319             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39320             y : minY
39321         });
39322
39323         pos.push({
39324             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39325             y : minY + (this.unitWidth + this.gutter) * 2
39326         });
39327         
39328         return pos;
39329         
39330     },
39331     
39332     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
39333     {
39334         var pos = [];
39335         
39336         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39337             
39338             pos.push({
39339                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39340                 y : minY
39341             });
39342
39343             pos.push({
39344                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39345                 y : minY + (this.unitWidth + this.gutter) * 1
39346             });
39347             
39348             pos.push({
39349                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39350                 y : minY + (this.unitWidth + this.gutter) * 2
39351             });
39352             
39353             return pos;
39354             
39355         }
39356         
39357         if(box[0].size == 'xs' && box[1].size == 'xs'){
39358             
39359             pos.push({
39360                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39361                 y : minY
39362             });
39363
39364             pos.push({
39365                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39366                 y : minY
39367             });
39368             
39369             pos.push({
39370                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39371                 y : minY + (this.unitWidth + this.gutter) * 1
39372             });
39373             
39374             return pos;
39375             
39376         }
39377         
39378         pos.push({
39379             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39380             y : minY
39381         });
39382
39383         pos.push({
39384             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39385             y : minY + (this.unitWidth + this.gutter) * 2
39386         });
39387
39388         pos.push({
39389             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39390             y : minY + (this.unitWidth + this.gutter) * 2
39391         });
39392             
39393         return pos;
39394         
39395     },
39396     
39397     getHorizontalFourBoxColPositions : function(maxX, minY, box)
39398     {
39399         var pos = [];
39400         
39401         if(box[0].size == 'xs'){
39402             
39403             pos.push({
39404                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39405                 y : minY
39406             });
39407
39408             pos.push({
39409                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39410                 y : minY
39411             });
39412             
39413             pos.push({
39414                 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),
39415                 y : minY
39416             });
39417             
39418             pos.push({
39419                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
39420                 y : minY + (this.unitWidth + this.gutter) * 1
39421             });
39422             
39423             return pos;
39424             
39425         }
39426         
39427         pos.push({
39428             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39429             y : minY
39430         });
39431         
39432         pos.push({
39433             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39434             y : minY + (this.unitWidth + this.gutter) * 2
39435         });
39436         
39437         pos.push({
39438             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39439             y : minY + (this.unitWidth + this.gutter) * 2
39440         });
39441         
39442         pos.push({
39443             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),
39444             y : minY + (this.unitWidth + this.gutter) * 2
39445         });
39446
39447         return pos;
39448         
39449     },
39450     
39451     /**
39452     * remove a Masonry Brick
39453     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
39454     */
39455     removeBrick : function(brick_id)
39456     {
39457         if (!brick_id) {
39458             return;
39459         }
39460         
39461         for (var i = 0; i<this.bricks.length; i++) {
39462             if (this.bricks[i].id == brick_id) {
39463                 this.bricks.splice(i,1);
39464                 this.el.dom.removeChild(Roo.get(brick_id).dom);
39465                 this.initial();
39466             }
39467         }
39468     },
39469     
39470     /**
39471     * adds a Masonry Brick
39472     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39473     */
39474     addBrick : function(cfg)
39475     {
39476         var cn = new Roo.bootstrap.MasonryBrick(cfg);
39477         //this.register(cn);
39478         cn.parentId = this.id;
39479         cn.render(this.el);
39480         return cn;
39481     },
39482     
39483     /**
39484     * register a Masonry Brick
39485     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39486     */
39487     
39488     register : function(brick)
39489     {
39490         this.bricks.push(brick);
39491         brick.masonryId = this.id;
39492     },
39493     
39494     /**
39495     * clear all the Masonry Brick
39496     */
39497     clearAll : function()
39498     {
39499         this.bricks = [];
39500         //this.getChildContainer().dom.innerHTML = "";
39501         this.el.dom.innerHTML = '';
39502     },
39503     
39504     getSelected : function()
39505     {
39506         if (!this.selectedBrick) {
39507             return false;
39508         }
39509         
39510         return this.selectedBrick;
39511     }
39512 });
39513
39514 Roo.apply(Roo.bootstrap.LayoutMasonry, {
39515     
39516     groups: {},
39517      /**
39518     * register a Masonry Layout
39519     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
39520     */
39521     
39522     register : function(layout)
39523     {
39524         this.groups[layout.id] = layout;
39525     },
39526     /**
39527     * fetch a  Masonry Layout based on the masonry layout ID
39528     * @param {string} the masonry layout to add
39529     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
39530     */
39531     
39532     get: function(layout_id) {
39533         if (typeof(this.groups[layout_id]) == 'undefined') {
39534             return false;
39535         }
39536         return this.groups[layout_id] ;
39537     }
39538     
39539     
39540     
39541 });
39542
39543  
39544
39545  /**
39546  *
39547  * This is based on 
39548  * http://masonry.desandro.com
39549  *
39550  * The idea is to render all the bricks based on vertical width...
39551  *
39552  * The original code extends 'outlayer' - we might need to use that....
39553  * 
39554  */
39555
39556
39557 /**
39558  * @class Roo.bootstrap.LayoutMasonryAuto
39559  * @extends Roo.bootstrap.Component
39560  * Bootstrap Layout Masonry class
39561  * 
39562  * @constructor
39563  * Create a new Element
39564  * @param {Object} config The config object
39565  */
39566
39567 Roo.bootstrap.LayoutMasonryAuto = function(config){
39568     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
39569 };
39570
39571 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
39572     
39573       /**
39574      * @cfg {Boolean} isFitWidth  - resize the width..
39575      */   
39576     isFitWidth : false,  // options..
39577     /**
39578      * @cfg {Boolean} isOriginLeft = left align?
39579      */   
39580     isOriginLeft : true,
39581     /**
39582      * @cfg {Boolean} isOriginTop = top align?
39583      */   
39584     isOriginTop : false,
39585     /**
39586      * @cfg {Boolean} isLayoutInstant = no animation?
39587      */   
39588     isLayoutInstant : false, // needed?
39589     /**
39590      * @cfg {Boolean} isResizingContainer = not sure if this is used..
39591      */   
39592     isResizingContainer : true,
39593     /**
39594      * @cfg {Number} columnWidth  width of the columns 
39595      */   
39596     
39597     columnWidth : 0,
39598     
39599     /**
39600      * @cfg {Number} maxCols maximum number of columns
39601      */   
39602     
39603     maxCols: 0,
39604     /**
39605      * @cfg {Number} padHeight padding below box..
39606      */   
39607     
39608     padHeight : 10, 
39609     
39610     /**
39611      * @cfg {Boolean} isAutoInitial defalut true
39612      */   
39613     
39614     isAutoInitial : true, 
39615     
39616     // private?
39617     gutter : 0,
39618     
39619     containerWidth: 0,
39620     initialColumnWidth : 0,
39621     currentSize : null,
39622     
39623     colYs : null, // array.
39624     maxY : 0,
39625     padWidth: 10,
39626     
39627     
39628     tag: 'div',
39629     cls: '',
39630     bricks: null, //CompositeElement
39631     cols : 0, // array?
39632     // element : null, // wrapped now this.el
39633     _isLayoutInited : null, 
39634     
39635     
39636     getAutoCreate : function(){
39637         
39638         var cfg = {
39639             tag: this.tag,
39640             cls: 'blog-masonary-wrapper ' + this.cls,
39641             cn : {
39642                 cls : 'mas-boxes masonary'
39643             }
39644         };
39645         
39646         return cfg;
39647     },
39648     
39649     getChildContainer: function( )
39650     {
39651         if (this.boxesEl) {
39652             return this.boxesEl;
39653         }
39654         
39655         this.boxesEl = this.el.select('.mas-boxes').first();
39656         
39657         return this.boxesEl;
39658     },
39659     
39660     
39661     initEvents : function()
39662     {
39663         var _this = this;
39664         
39665         if(this.isAutoInitial){
39666             Roo.log('hook children rendered');
39667             this.on('childrenrendered', function() {
39668                 Roo.log('children rendered');
39669                 _this.initial();
39670             } ,this);
39671         }
39672         
39673     },
39674     
39675     initial : function()
39676     {
39677         this.reloadItems();
39678
39679         this.currentSize = this.el.getBox(true);
39680
39681         /// was window resize... - let's see if this works..
39682         Roo.EventManager.onWindowResize(this.resize, this); 
39683
39684         if(!this.isAutoInitial){
39685             this.layout();
39686             return;
39687         }
39688         
39689         this.layout.defer(500,this);
39690     },
39691     
39692     reloadItems: function()
39693     {
39694         this.bricks = this.el.select('.masonry-brick', true);
39695         
39696         this.bricks.each(function(b) {
39697             //Roo.log(b.getSize());
39698             if (!b.attr('originalwidth')) {
39699                 b.attr('originalwidth',  b.getSize().width);
39700             }
39701             
39702         });
39703         
39704         Roo.log(this.bricks.elements.length);
39705     },
39706     
39707     resize : function()
39708     {
39709         Roo.log('resize');
39710         var cs = this.el.getBox(true);
39711         
39712         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
39713             Roo.log("no change in with or X");
39714             return;
39715         }
39716         this.currentSize = cs;
39717         this.layout();
39718     },
39719     
39720     layout : function()
39721     {
39722          Roo.log('layout');
39723         this._resetLayout();
39724         //this._manageStamps();
39725       
39726         // don't animate first layout
39727         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
39728         this.layoutItems( isInstant );
39729       
39730         // flag for initalized
39731         this._isLayoutInited = true;
39732     },
39733     
39734     layoutItems : function( isInstant )
39735     {
39736         //var items = this._getItemsForLayout( this.items );
39737         // original code supports filtering layout items.. we just ignore it..
39738         
39739         this._layoutItems( this.bricks , isInstant );
39740       
39741         this._postLayout();
39742     },
39743     _layoutItems : function ( items , isInstant)
39744     {
39745        //this.fireEvent( 'layout', this, items );
39746     
39747
39748         if ( !items || !items.elements.length ) {
39749           // no items, emit event with empty array
39750             return;
39751         }
39752
39753         var queue = [];
39754         items.each(function(item) {
39755             Roo.log("layout item");
39756             Roo.log(item);
39757             // get x/y object from method
39758             var position = this._getItemLayoutPosition( item );
39759             // enqueue
39760             position.item = item;
39761             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
39762             queue.push( position );
39763         }, this);
39764       
39765         this._processLayoutQueue( queue );
39766     },
39767     /** Sets position of item in DOM
39768     * @param {Element} item
39769     * @param {Number} x - horizontal position
39770     * @param {Number} y - vertical position
39771     * @param {Boolean} isInstant - disables transitions
39772     */
39773     _processLayoutQueue : function( queue )
39774     {
39775         for ( var i=0, len = queue.length; i < len; i++ ) {
39776             var obj = queue[i];
39777             obj.item.position('absolute');
39778             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
39779         }
39780     },
39781       
39782     
39783     /**
39784     * Any logic you want to do after each layout,
39785     * i.e. size the container
39786     */
39787     _postLayout : function()
39788     {
39789         this.resizeContainer();
39790     },
39791     
39792     resizeContainer : function()
39793     {
39794         if ( !this.isResizingContainer ) {
39795             return;
39796         }
39797         var size = this._getContainerSize();
39798         if ( size ) {
39799             this.el.setSize(size.width,size.height);
39800             this.boxesEl.setSize(size.width,size.height);
39801         }
39802     },
39803     
39804     
39805     
39806     _resetLayout : function()
39807     {
39808         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
39809         this.colWidth = this.el.getWidth();
39810         //this.gutter = this.el.getWidth(); 
39811         
39812         this.measureColumns();
39813
39814         // reset column Y
39815         var i = this.cols;
39816         this.colYs = [];
39817         while (i--) {
39818             this.colYs.push( 0 );
39819         }
39820     
39821         this.maxY = 0;
39822     },
39823
39824     measureColumns : function()
39825     {
39826         this.getContainerWidth();
39827       // if columnWidth is 0, default to outerWidth of first item
39828         if ( !this.columnWidth ) {
39829             var firstItem = this.bricks.first();
39830             Roo.log(firstItem);
39831             this.columnWidth  = this.containerWidth;
39832             if (firstItem && firstItem.attr('originalwidth') ) {
39833                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
39834             }
39835             // columnWidth fall back to item of first element
39836             Roo.log("set column width?");
39837                         this.initialColumnWidth = this.columnWidth  ;
39838
39839             // if first elem has no width, default to size of container
39840             
39841         }
39842         
39843         
39844         if (this.initialColumnWidth) {
39845             this.columnWidth = this.initialColumnWidth;
39846         }
39847         
39848         
39849             
39850         // column width is fixed at the top - however if container width get's smaller we should
39851         // reduce it...
39852         
39853         // this bit calcs how man columns..
39854             
39855         var columnWidth = this.columnWidth += this.gutter;
39856       
39857         // calculate columns
39858         var containerWidth = this.containerWidth + this.gutter;
39859         
39860         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
39861         // fix rounding errors, typically with gutters
39862         var excess = columnWidth - containerWidth % columnWidth;
39863         
39864         
39865         // if overshoot is less than a pixel, round up, otherwise floor it
39866         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
39867         cols = Math[ mathMethod ]( cols );
39868         this.cols = Math.max( cols, 1 );
39869         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39870         
39871          // padding positioning..
39872         var totalColWidth = this.cols * this.columnWidth;
39873         var padavail = this.containerWidth - totalColWidth;
39874         // so for 2 columns - we need 3 'pads'
39875         
39876         var padNeeded = (1+this.cols) * this.padWidth;
39877         
39878         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
39879         
39880         this.columnWidth += padExtra
39881         //this.padWidth = Math.floor(padavail /  ( this.cols));
39882         
39883         // adjust colum width so that padding is fixed??
39884         
39885         // we have 3 columns ... total = width * 3
39886         // we have X left over... that should be used by 
39887         
39888         //if (this.expandC) {
39889             
39890         //}
39891         
39892         
39893         
39894     },
39895     
39896     getContainerWidth : function()
39897     {
39898        /* // container is parent if fit width
39899         var container = this.isFitWidth ? this.element.parentNode : this.element;
39900         // check that this.size and size are there
39901         // IE8 triggers resize on body size change, so they might not be
39902         
39903         var size = getSize( container );  //FIXME
39904         this.containerWidth = size && size.innerWidth; //FIXME
39905         */
39906          
39907         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
39908         
39909     },
39910     
39911     _getItemLayoutPosition : function( item )  // what is item?
39912     {
39913         // we resize the item to our columnWidth..
39914       
39915         item.setWidth(this.columnWidth);
39916         item.autoBoxAdjust  = false;
39917         
39918         var sz = item.getSize();
39919  
39920         // how many columns does this brick span
39921         var remainder = this.containerWidth % this.columnWidth;
39922         
39923         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
39924         // round if off by 1 pixel, otherwise use ceil
39925         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
39926         colSpan = Math.min( colSpan, this.cols );
39927         
39928         // normally this should be '1' as we dont' currently allow multi width columns..
39929         
39930         var colGroup = this._getColGroup( colSpan );
39931         // get the minimum Y value from the columns
39932         var minimumY = Math.min.apply( Math, colGroup );
39933         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
39934         
39935         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
39936          
39937         // position the brick
39938         var position = {
39939             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
39940             y: this.currentSize.y + minimumY + this.padHeight
39941         };
39942         
39943         Roo.log(position);
39944         // apply setHeight to necessary columns
39945         var setHeight = minimumY + sz.height + this.padHeight;
39946         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
39947         
39948         var setSpan = this.cols + 1 - colGroup.length;
39949         for ( var i = 0; i < setSpan; i++ ) {
39950           this.colYs[ shortColIndex + i ] = setHeight ;
39951         }
39952       
39953         return position;
39954     },
39955     
39956     /**
39957      * @param {Number} colSpan - number of columns the element spans
39958      * @returns {Array} colGroup
39959      */
39960     _getColGroup : function( colSpan )
39961     {
39962         if ( colSpan < 2 ) {
39963           // if brick spans only one column, use all the column Ys
39964           return this.colYs;
39965         }
39966       
39967         var colGroup = [];
39968         // how many different places could this brick fit horizontally
39969         var groupCount = this.cols + 1 - colSpan;
39970         // for each group potential horizontal position
39971         for ( var i = 0; i < groupCount; i++ ) {
39972           // make an array of colY values for that one group
39973           var groupColYs = this.colYs.slice( i, i + colSpan );
39974           // and get the max value of the array
39975           colGroup[i] = Math.max.apply( Math, groupColYs );
39976         }
39977         return colGroup;
39978     },
39979     /*
39980     _manageStamp : function( stamp )
39981     {
39982         var stampSize =  stamp.getSize();
39983         var offset = stamp.getBox();
39984         // get the columns that this stamp affects
39985         var firstX = this.isOriginLeft ? offset.x : offset.right;
39986         var lastX = firstX + stampSize.width;
39987         var firstCol = Math.floor( firstX / this.columnWidth );
39988         firstCol = Math.max( 0, firstCol );
39989         
39990         var lastCol = Math.floor( lastX / this.columnWidth );
39991         // lastCol should not go over if multiple of columnWidth #425
39992         lastCol -= lastX % this.columnWidth ? 0 : 1;
39993         lastCol = Math.min( this.cols - 1, lastCol );
39994         
39995         // set colYs to bottom of the stamp
39996         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
39997             stampSize.height;
39998             
39999         for ( var i = firstCol; i <= lastCol; i++ ) {
40000           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
40001         }
40002     },
40003     */
40004     
40005     _getContainerSize : function()
40006     {
40007         this.maxY = Math.max.apply( Math, this.colYs );
40008         var size = {
40009             height: this.maxY
40010         };
40011       
40012         if ( this.isFitWidth ) {
40013             size.width = this._getContainerFitWidth();
40014         }
40015       
40016         return size;
40017     },
40018     
40019     _getContainerFitWidth : function()
40020     {
40021         var unusedCols = 0;
40022         // count unused columns
40023         var i = this.cols;
40024         while ( --i ) {
40025           if ( this.colYs[i] !== 0 ) {
40026             break;
40027           }
40028           unusedCols++;
40029         }
40030         // fit container to columns that have been used
40031         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40032     },
40033     
40034     needsResizeLayout : function()
40035     {
40036         var previousWidth = this.containerWidth;
40037         this.getContainerWidth();
40038         return previousWidth !== this.containerWidth;
40039     }
40040  
40041 });
40042
40043  
40044
40045  /*
40046  * - LGPL
40047  *
40048  * element
40049  * 
40050  */
40051
40052 /**
40053  * @class Roo.bootstrap.MasonryBrick
40054  * @extends Roo.bootstrap.Component
40055  * Bootstrap MasonryBrick class
40056  * 
40057  * @constructor
40058  * Create a new MasonryBrick
40059  * @param {Object} config The config object
40060  */
40061
40062 Roo.bootstrap.MasonryBrick = function(config){
40063     
40064     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40065     
40066     Roo.bootstrap.MasonryBrick.register(this);
40067     
40068     this.addEvents({
40069         // raw events
40070         /**
40071          * @event click
40072          * When a MasonryBrick is clcik
40073          * @param {Roo.bootstrap.MasonryBrick} this
40074          * @param {Roo.EventObject} e
40075          */
40076         "click" : true
40077     });
40078 };
40079
40080 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
40081     
40082     /**
40083      * @cfg {String} title
40084      */   
40085     title : '',
40086     /**
40087      * @cfg {String} html
40088      */   
40089     html : '',
40090     /**
40091      * @cfg {String} bgimage
40092      */   
40093     bgimage : '',
40094     /**
40095      * @cfg {String} videourl
40096      */   
40097     videourl : '',
40098     /**
40099      * @cfg {String} cls
40100      */   
40101     cls : '',
40102     /**
40103      * @cfg {String} href
40104      */   
40105     href : '',
40106     /**
40107      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40108      */   
40109     size : 'xs',
40110     
40111     /**
40112      * @cfg {String} placetitle (center|bottom)
40113      */   
40114     placetitle : '',
40115     
40116     /**
40117      * @cfg {Boolean} isFitContainer defalut true
40118      */   
40119     isFitContainer : true, 
40120     
40121     /**
40122      * @cfg {Boolean} preventDefault defalut false
40123      */   
40124     preventDefault : false, 
40125     
40126     /**
40127      * @cfg {Boolean} inverse defalut false
40128      */   
40129     maskInverse : false, 
40130     
40131     getAutoCreate : function()
40132     {
40133         if(!this.isFitContainer){
40134             return this.getSplitAutoCreate();
40135         }
40136         
40137         var cls = 'masonry-brick masonry-brick-full';
40138         
40139         if(this.href.length){
40140             cls += ' masonry-brick-link';
40141         }
40142         
40143         if(this.bgimage.length){
40144             cls += ' masonry-brick-image';
40145         }
40146         
40147         if(this.maskInverse){
40148             cls += ' mask-inverse';
40149         }
40150         
40151         if(!this.html.length && !this.maskInverse && !this.videourl.length){
40152             cls += ' enable-mask';
40153         }
40154         
40155         if(this.size){
40156             cls += ' masonry-' + this.size + '-brick';
40157         }
40158         
40159         if(this.placetitle.length){
40160             
40161             switch (this.placetitle) {
40162                 case 'center' :
40163                     cls += ' masonry-center-title';
40164                     break;
40165                 case 'bottom' :
40166                     cls += ' masonry-bottom-title';
40167                     break;
40168                 default:
40169                     break;
40170             }
40171             
40172         } else {
40173             if(!this.html.length && !this.bgimage.length){
40174                 cls += ' masonry-center-title';
40175             }
40176
40177             if(!this.html.length && this.bgimage.length){
40178                 cls += ' masonry-bottom-title';
40179             }
40180         }
40181         
40182         if(this.cls){
40183             cls += ' ' + this.cls;
40184         }
40185         
40186         var cfg = {
40187             tag: (this.href.length) ? 'a' : 'div',
40188             cls: cls,
40189             cn: [
40190                 {
40191                     tag: 'div',
40192                     cls: 'masonry-brick-mask'
40193                 },
40194                 {
40195                     tag: 'div',
40196                     cls: 'masonry-brick-paragraph',
40197                     cn: []
40198                 }
40199             ]
40200         };
40201         
40202         if(this.href.length){
40203             cfg.href = this.href;
40204         }
40205         
40206         var cn = cfg.cn[1].cn;
40207         
40208         if(this.title.length){
40209             cn.push({
40210                 tag: 'h4',
40211                 cls: 'masonry-brick-title',
40212                 html: this.title
40213             });
40214         }
40215         
40216         if(this.html.length){
40217             cn.push({
40218                 tag: 'p',
40219                 cls: 'masonry-brick-text',
40220                 html: this.html
40221             });
40222         }
40223         
40224         if (!this.title.length && !this.html.length) {
40225             cfg.cn[1].cls += ' hide';
40226         }
40227         
40228         if(this.bgimage.length){
40229             cfg.cn.push({
40230                 tag: 'img',
40231                 cls: 'masonry-brick-image-view',
40232                 src: this.bgimage
40233             });
40234         }
40235         
40236         if(this.videourl.length){
40237             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40238             // youtube support only?
40239             cfg.cn.push({
40240                 tag: 'iframe',
40241                 cls: 'masonry-brick-image-view',
40242                 src: vurl,
40243                 frameborder : 0,
40244                 allowfullscreen : true
40245             });
40246         }
40247         
40248         return cfg;
40249         
40250     },
40251     
40252     getSplitAutoCreate : function()
40253     {
40254         var cls = 'masonry-brick masonry-brick-split';
40255         
40256         if(this.href.length){
40257             cls += ' masonry-brick-link';
40258         }
40259         
40260         if(this.bgimage.length){
40261             cls += ' masonry-brick-image';
40262         }
40263         
40264         if(this.size){
40265             cls += ' masonry-' + this.size + '-brick';
40266         }
40267         
40268         switch (this.placetitle) {
40269             case 'center' :
40270                 cls += ' masonry-center-title';
40271                 break;
40272             case 'bottom' :
40273                 cls += ' masonry-bottom-title';
40274                 break;
40275             default:
40276                 if(!this.bgimage.length){
40277                     cls += ' masonry-center-title';
40278                 }
40279
40280                 if(this.bgimage.length){
40281                     cls += ' masonry-bottom-title';
40282                 }
40283                 break;
40284         }
40285         
40286         if(this.cls){
40287             cls += ' ' + this.cls;
40288         }
40289         
40290         var cfg = {
40291             tag: (this.href.length) ? 'a' : 'div',
40292             cls: cls,
40293             cn: [
40294                 {
40295                     tag: 'div',
40296                     cls: 'masonry-brick-split-head',
40297                     cn: [
40298                         {
40299                             tag: 'div',
40300                             cls: 'masonry-brick-paragraph',
40301                             cn: []
40302                         }
40303                     ]
40304                 },
40305                 {
40306                     tag: 'div',
40307                     cls: 'masonry-brick-split-body',
40308                     cn: []
40309                 }
40310             ]
40311         };
40312         
40313         if(this.href.length){
40314             cfg.href = this.href;
40315         }
40316         
40317         if(this.title.length){
40318             cfg.cn[0].cn[0].cn.push({
40319                 tag: 'h4',
40320                 cls: 'masonry-brick-title',
40321                 html: this.title
40322             });
40323         }
40324         
40325         if(this.html.length){
40326             cfg.cn[1].cn.push({
40327                 tag: 'p',
40328                 cls: 'masonry-brick-text',
40329                 html: this.html
40330             });
40331         }
40332
40333         if(this.bgimage.length){
40334             cfg.cn[0].cn.push({
40335                 tag: 'img',
40336                 cls: 'masonry-brick-image-view',
40337                 src: this.bgimage
40338             });
40339         }
40340         
40341         if(this.videourl.length){
40342             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40343             // youtube support only?
40344             cfg.cn[0].cn.cn.push({
40345                 tag: 'iframe',
40346                 cls: 'masonry-brick-image-view',
40347                 src: vurl,
40348                 frameborder : 0,
40349                 allowfullscreen : true
40350             });
40351         }
40352         
40353         return cfg;
40354     },
40355     
40356     initEvents: function() 
40357     {
40358         switch (this.size) {
40359             case 'xs' :
40360                 this.x = 1;
40361                 this.y = 1;
40362                 break;
40363             case 'sm' :
40364                 this.x = 2;
40365                 this.y = 2;
40366                 break;
40367             case 'md' :
40368             case 'md-left' :
40369             case 'md-right' :
40370                 this.x = 3;
40371                 this.y = 3;
40372                 break;
40373             case 'tall' :
40374                 this.x = 2;
40375                 this.y = 3;
40376                 break;
40377             case 'wide' :
40378                 this.x = 3;
40379                 this.y = 2;
40380                 break;
40381             case 'wide-thin' :
40382                 this.x = 3;
40383                 this.y = 1;
40384                 break;
40385                         
40386             default :
40387                 break;
40388         }
40389         
40390         if(Roo.isTouch){
40391             this.el.on('touchstart', this.onTouchStart, this);
40392             this.el.on('touchmove', this.onTouchMove, this);
40393             this.el.on('touchend', this.onTouchEnd, this);
40394             this.el.on('contextmenu', this.onContextMenu, this);
40395         } else {
40396             this.el.on('mouseenter'  ,this.enter, this);
40397             this.el.on('mouseleave', this.leave, this);
40398             this.el.on('click', this.onClick, this);
40399         }
40400         
40401         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
40402             this.parent().bricks.push(this);   
40403         }
40404         
40405     },
40406     
40407     onClick: function(e, el)
40408     {
40409         var time = this.endTimer - this.startTimer;
40410         // Roo.log(e.preventDefault());
40411         if(Roo.isTouch){
40412             if(time > 1000){
40413                 e.preventDefault();
40414                 return;
40415             }
40416         }
40417         
40418         if(!this.preventDefault){
40419             return;
40420         }
40421         
40422         e.preventDefault();
40423         
40424         if (this.activeClass != '') {
40425             this.selectBrick();
40426         }
40427         
40428         this.fireEvent('click', this, e);
40429     },
40430     
40431     enter: function(e, el)
40432     {
40433         e.preventDefault();
40434         
40435         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40436             return;
40437         }
40438         
40439         if(this.bgimage.length && this.html.length){
40440             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40441         }
40442     },
40443     
40444     leave: function(e, el)
40445     {
40446         e.preventDefault();
40447         
40448         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
40449             return;
40450         }
40451         
40452         if(this.bgimage.length && this.html.length){
40453             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40454         }
40455     },
40456     
40457     onTouchStart: function(e, el)
40458     {
40459 //        e.preventDefault();
40460         
40461         this.touchmoved = false;
40462         
40463         if(!this.isFitContainer){
40464             return;
40465         }
40466         
40467         if(!this.bgimage.length || !this.html.length){
40468             return;
40469         }
40470         
40471         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40472         
40473         this.timer = new Date().getTime();
40474         
40475     },
40476     
40477     onTouchMove: function(e, el)
40478     {
40479         this.touchmoved = true;
40480     },
40481     
40482     onContextMenu : function(e,el)
40483     {
40484         e.preventDefault();
40485         e.stopPropagation();
40486         return false;
40487     },
40488     
40489     onTouchEnd: function(e, el)
40490     {
40491 //        e.preventDefault();
40492         
40493         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
40494         
40495             this.leave(e,el);
40496             
40497             return;
40498         }
40499         
40500         if(!this.bgimage.length || !this.html.length){
40501             
40502             if(this.href.length){
40503                 window.location.href = this.href;
40504             }
40505             
40506             return;
40507         }
40508         
40509         if(!this.isFitContainer){
40510             return;
40511         }
40512         
40513         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40514         
40515         window.location.href = this.href;
40516     },
40517     
40518     //selection on single brick only
40519     selectBrick : function() {
40520         
40521         if (!this.parentId) {
40522             return;
40523         }
40524         
40525         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
40526         var index = m.selectedBrick.indexOf(this.id);
40527         
40528         if ( index > -1) {
40529             m.selectedBrick.splice(index,1);
40530             this.el.removeClass(this.activeClass);
40531             return;
40532         }
40533         
40534         for(var i = 0; i < m.selectedBrick.length; i++) {
40535             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
40536             b.el.removeClass(b.activeClass);
40537         }
40538         
40539         m.selectedBrick = [];
40540         
40541         m.selectedBrick.push(this.id);
40542         this.el.addClass(this.activeClass);
40543         return;
40544     },
40545     
40546     isSelected : function(){
40547         return this.el.hasClass(this.activeClass);
40548         
40549     }
40550 });
40551
40552 Roo.apply(Roo.bootstrap.MasonryBrick, {
40553     
40554     //groups: {},
40555     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
40556      /**
40557     * register a Masonry Brick
40558     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40559     */
40560     
40561     register : function(brick)
40562     {
40563         //this.groups[brick.id] = brick;
40564         this.groups.add(brick.id, brick);
40565     },
40566     /**
40567     * fetch a  masonry brick based on the masonry brick ID
40568     * @param {string} the masonry brick to add
40569     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
40570     */
40571     
40572     get: function(brick_id) 
40573     {
40574         // if (typeof(this.groups[brick_id]) == 'undefined') {
40575         //     return false;
40576         // }
40577         // return this.groups[brick_id] ;
40578         
40579         if(this.groups.key(brick_id)) {
40580             return this.groups.key(brick_id);
40581         }
40582         
40583         return false;
40584     }
40585     
40586     
40587     
40588 });
40589
40590  /*
40591  * - LGPL
40592  *
40593  * element
40594  * 
40595  */
40596
40597 /**
40598  * @class Roo.bootstrap.Brick
40599  * @extends Roo.bootstrap.Component
40600  * Bootstrap Brick class
40601  * 
40602  * @constructor
40603  * Create a new Brick
40604  * @param {Object} config The config object
40605  */
40606
40607 Roo.bootstrap.Brick = function(config){
40608     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
40609     
40610     this.addEvents({
40611         // raw events
40612         /**
40613          * @event click
40614          * When a Brick is click
40615          * @param {Roo.bootstrap.Brick} this
40616          * @param {Roo.EventObject} e
40617          */
40618         "click" : true
40619     });
40620 };
40621
40622 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
40623     
40624     /**
40625      * @cfg {String} title
40626      */   
40627     title : '',
40628     /**
40629      * @cfg {String} html
40630      */   
40631     html : '',
40632     /**
40633      * @cfg {String} bgimage
40634      */   
40635     bgimage : '',
40636     /**
40637      * @cfg {String} cls
40638      */   
40639     cls : '',
40640     /**
40641      * @cfg {String} href
40642      */   
40643     href : '',
40644     /**
40645      * @cfg {String} video
40646      */   
40647     video : '',
40648     /**
40649      * @cfg {Boolean} square
40650      */   
40651     square : true,
40652     
40653     getAutoCreate : function()
40654     {
40655         var cls = 'roo-brick';
40656         
40657         if(this.href.length){
40658             cls += ' roo-brick-link';
40659         }
40660         
40661         if(this.bgimage.length){
40662             cls += ' roo-brick-image';
40663         }
40664         
40665         if(!this.html.length && !this.bgimage.length){
40666             cls += ' roo-brick-center-title';
40667         }
40668         
40669         if(!this.html.length && this.bgimage.length){
40670             cls += ' roo-brick-bottom-title';
40671         }
40672         
40673         if(this.cls){
40674             cls += ' ' + this.cls;
40675         }
40676         
40677         var cfg = {
40678             tag: (this.href.length) ? 'a' : 'div',
40679             cls: cls,
40680             cn: [
40681                 {
40682                     tag: 'div',
40683                     cls: 'roo-brick-paragraph',
40684                     cn: []
40685                 }
40686             ]
40687         };
40688         
40689         if(this.href.length){
40690             cfg.href = this.href;
40691         }
40692         
40693         var cn = cfg.cn[0].cn;
40694         
40695         if(this.title.length){
40696             cn.push({
40697                 tag: 'h4',
40698                 cls: 'roo-brick-title',
40699                 html: this.title
40700             });
40701         }
40702         
40703         if(this.html.length){
40704             cn.push({
40705                 tag: 'p',
40706                 cls: 'roo-brick-text',
40707                 html: this.html
40708             });
40709         } else {
40710             cn.cls += ' hide';
40711         }
40712         
40713         if(this.bgimage.length){
40714             cfg.cn.push({
40715                 tag: 'img',
40716                 cls: 'roo-brick-image-view',
40717                 src: this.bgimage
40718             });
40719         }
40720         
40721         return cfg;
40722     },
40723     
40724     initEvents: function() 
40725     {
40726         if(this.title.length || this.html.length){
40727             this.el.on('mouseenter'  ,this.enter, this);
40728             this.el.on('mouseleave', this.leave, this);
40729         }
40730         
40731         Roo.EventManager.onWindowResize(this.resize, this); 
40732         
40733         if(this.bgimage.length){
40734             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
40735             this.imageEl.on('load', this.onImageLoad, this);
40736             return;
40737         }
40738         
40739         this.resize();
40740     },
40741     
40742     onImageLoad : function()
40743     {
40744         this.resize();
40745     },
40746     
40747     resize : function()
40748     {
40749         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
40750         
40751         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
40752         
40753         if(this.bgimage.length){
40754             var image = this.el.select('.roo-brick-image-view', true).first();
40755             
40756             image.setWidth(paragraph.getWidth());
40757             
40758             if(this.square){
40759                 image.setHeight(paragraph.getWidth());
40760             }
40761             
40762             this.el.setHeight(image.getHeight());
40763             paragraph.setHeight(image.getHeight());
40764             
40765         }
40766         
40767     },
40768     
40769     enter: function(e, el)
40770     {
40771         e.preventDefault();
40772         
40773         if(this.bgimage.length){
40774             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
40775             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
40776         }
40777     },
40778     
40779     leave: function(e, el)
40780     {
40781         e.preventDefault();
40782         
40783         if(this.bgimage.length){
40784             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
40785             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
40786         }
40787     }
40788     
40789 });
40790
40791  
40792
40793  /*
40794  * - LGPL
40795  *
40796  * Number field 
40797  */
40798
40799 /**
40800  * @class Roo.bootstrap.form.NumberField
40801  * @extends Roo.bootstrap.form.Input
40802  * Bootstrap NumberField class
40803  * 
40804  * 
40805  * 
40806  * 
40807  * @constructor
40808  * Create a new NumberField
40809  * @param {Object} config The config object
40810  */
40811
40812 Roo.bootstrap.form.NumberField = function(config){
40813     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
40814 };
40815
40816 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
40817     
40818     /**
40819      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40820      */
40821     allowDecimals : true,
40822     /**
40823      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40824      */
40825     decimalSeparator : ".",
40826     /**
40827      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40828      */
40829     decimalPrecision : 2,
40830     /**
40831      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40832      */
40833     allowNegative : true,
40834     
40835     /**
40836      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40837      */
40838     allowZero: true,
40839     /**
40840      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40841      */
40842     minValue : Number.NEGATIVE_INFINITY,
40843     /**
40844      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40845      */
40846     maxValue : Number.MAX_VALUE,
40847     /**
40848      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40849      */
40850     minText : "The minimum value for this field is {0}",
40851     /**
40852      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40853      */
40854     maxText : "The maximum value for this field is {0}",
40855     /**
40856      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40857      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40858      */
40859     nanText : "{0} is not a valid number",
40860     /**
40861      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40862      */
40863     thousandsDelimiter : false,
40864     /**
40865      * @cfg {String} valueAlign alignment of value
40866      */
40867     valueAlign : "left",
40868
40869     getAutoCreate : function()
40870     {
40871         var hiddenInput = {
40872             tag: 'input',
40873             type: 'hidden',
40874             id: Roo.id(),
40875             cls: 'hidden-number-input'
40876         };
40877         
40878         if (this.name) {
40879             hiddenInput.name = this.name;
40880         }
40881         
40882         this.name = '';
40883         
40884         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
40885         
40886         this.name = hiddenInput.name;
40887         
40888         if(cfg.cn.length > 0) {
40889             cfg.cn.push(hiddenInput);
40890         }
40891         
40892         return cfg;
40893     },
40894
40895     // private
40896     initEvents : function()
40897     {   
40898         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
40899         
40900         var allowed = "0123456789";
40901         
40902         if(this.allowDecimals){
40903             allowed += this.decimalSeparator;
40904         }
40905         
40906         if(this.allowNegative){
40907             allowed += "-";
40908         }
40909         
40910         if(this.thousandsDelimiter) {
40911             allowed += ",";
40912         }
40913         
40914         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40915         
40916         var keyPress = function(e){
40917             
40918             var k = e.getKey();
40919             
40920             var c = e.getCharCode();
40921             
40922             if(
40923                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40924                     allowed.indexOf(String.fromCharCode(c)) === -1
40925             ){
40926                 e.stopEvent();
40927                 return;
40928             }
40929             
40930             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40931                 return;
40932             }
40933             
40934             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40935                 e.stopEvent();
40936             }
40937         };
40938         
40939         this.el.on("keypress", keyPress, this);
40940     },
40941     
40942     validateValue : function(value)
40943     {
40944         
40945         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
40946             return false;
40947         }
40948         
40949         var num = this.parseValue(value);
40950         
40951         if(isNaN(num)){
40952             this.markInvalid(String.format(this.nanText, value));
40953             return false;
40954         }
40955         
40956         if(num < this.minValue){
40957             this.markInvalid(String.format(this.minText, this.minValue));
40958             return false;
40959         }
40960         
40961         if(num > this.maxValue){
40962             this.markInvalid(String.format(this.maxText, this.maxValue));
40963             return false;
40964         }
40965         
40966         return true;
40967     },
40968
40969     getValue : function()
40970     {
40971         var v = this.hiddenEl().getValue();
40972         
40973         return this.fixPrecision(this.parseValue(v));
40974     },
40975
40976     parseValue : function(value)
40977     {
40978         if(this.thousandsDelimiter) {
40979             value += "";
40980             r = new RegExp(",", "g");
40981             value = value.replace(r, "");
40982         }
40983         
40984         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40985         return isNaN(value) ? '' : value;
40986     },
40987
40988     fixPrecision : function(value)
40989     {
40990         if(this.thousandsDelimiter) {
40991             value += "";
40992             r = new RegExp(",", "g");
40993             value = value.replace(r, "");
40994         }
40995         
40996         var nan = isNaN(value);
40997         
40998         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40999             return nan ? '' : value;
41000         }
41001         return parseFloat(value).toFixed(this.decimalPrecision);
41002     },
41003
41004     setValue : function(v)
41005     {
41006         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41007         
41008         this.value = v;
41009         
41010         if(this.rendered){
41011             
41012             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41013             
41014             this.inputEl().dom.value = (v == '') ? '' :
41015                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41016             
41017             if(!this.allowZero && v === '0') {
41018                 this.hiddenEl().dom.value = '';
41019                 this.inputEl().dom.value = '';
41020             }
41021             
41022             this.validate();
41023         }
41024     },
41025
41026     decimalPrecisionFcn : function(v)
41027     {
41028         return Math.floor(v);
41029     },
41030
41031     beforeBlur : function()
41032     {
41033         var v = this.parseValue(this.getRawValue());
41034         
41035         if(v || v === 0 || v === ''){
41036             this.setValue(v);
41037         }
41038     },
41039     
41040     hiddenEl : function()
41041     {
41042         return this.el.select('input.hidden-number-input',true).first();
41043     }
41044     
41045 });
41046
41047  
41048
41049 /*
41050 * Licence: LGPL
41051 */
41052
41053 /**
41054  * @class Roo.bootstrap.DocumentSlider
41055  * @extends Roo.bootstrap.Component
41056  * Bootstrap DocumentSlider class
41057  * 
41058  * @constructor
41059  * Create a new DocumentViewer
41060  * @param {Object} config The config object
41061  */
41062
41063 Roo.bootstrap.DocumentSlider = function(config){
41064     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41065     
41066     this.files = [];
41067     
41068     this.addEvents({
41069         /**
41070          * @event initial
41071          * Fire after initEvent
41072          * @param {Roo.bootstrap.DocumentSlider} this
41073          */
41074         "initial" : true,
41075         /**
41076          * @event update
41077          * Fire after update
41078          * @param {Roo.bootstrap.DocumentSlider} this
41079          */
41080         "update" : true,
41081         /**
41082          * @event click
41083          * Fire after click
41084          * @param {Roo.bootstrap.DocumentSlider} this
41085          */
41086         "click" : true
41087     });
41088 };
41089
41090 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
41091     
41092     files : false,
41093     
41094     indicator : 0,
41095     
41096     getAutoCreate : function()
41097     {
41098         var cfg = {
41099             tag : 'div',
41100             cls : 'roo-document-slider',
41101             cn : [
41102                 {
41103                     tag : 'div',
41104                     cls : 'roo-document-slider-header',
41105                     cn : [
41106                         {
41107                             tag : 'div',
41108                             cls : 'roo-document-slider-header-title'
41109                         }
41110                     ]
41111                 },
41112                 {
41113                     tag : 'div',
41114                     cls : 'roo-document-slider-body',
41115                     cn : [
41116                         {
41117                             tag : 'div',
41118                             cls : 'roo-document-slider-prev',
41119                             cn : [
41120                                 {
41121                                     tag : 'i',
41122                                     cls : 'fa fa-chevron-left'
41123                                 }
41124                             ]
41125                         },
41126                         {
41127                             tag : 'div',
41128                             cls : 'roo-document-slider-thumb',
41129                             cn : [
41130                                 {
41131                                     tag : 'img',
41132                                     cls : 'roo-document-slider-image'
41133                                 }
41134                             ]
41135                         },
41136                         {
41137                             tag : 'div',
41138                             cls : 'roo-document-slider-next',
41139                             cn : [
41140                                 {
41141                                     tag : 'i',
41142                                     cls : 'fa fa-chevron-right'
41143                                 }
41144                             ]
41145                         }
41146                     ]
41147                 }
41148             ]
41149         };
41150         
41151         return cfg;
41152     },
41153     
41154     initEvents : function()
41155     {
41156         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41157         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41158         
41159         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41160         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41161         
41162         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41163         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41164         
41165         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41166         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41167         
41168         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41169         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41170         
41171         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41172         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41173         
41174         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41175         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41176         
41177         this.thumbEl.on('click', this.onClick, this);
41178         
41179         this.prevIndicator.on('click', this.prev, this);
41180         
41181         this.nextIndicator.on('click', this.next, this);
41182         
41183     },
41184     
41185     initial : function()
41186     {
41187         if(this.files.length){
41188             this.indicator = 1;
41189             this.update()
41190         }
41191         
41192         this.fireEvent('initial', this);
41193     },
41194     
41195     update : function()
41196     {
41197         this.imageEl.attr('src', this.files[this.indicator - 1]);
41198         
41199         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
41200         
41201         this.prevIndicator.show();
41202         
41203         if(this.indicator == 1){
41204             this.prevIndicator.hide();
41205         }
41206         
41207         this.nextIndicator.show();
41208         
41209         if(this.indicator == this.files.length){
41210             this.nextIndicator.hide();
41211         }
41212         
41213         this.thumbEl.scrollTo('top');
41214         
41215         this.fireEvent('update', this);
41216     },
41217     
41218     onClick : function(e)
41219     {
41220         e.preventDefault();
41221         
41222         this.fireEvent('click', this);
41223     },
41224     
41225     prev : function(e)
41226     {
41227         e.preventDefault();
41228         
41229         this.indicator = Math.max(1, this.indicator - 1);
41230         
41231         this.update();
41232     },
41233     
41234     next : function(e)
41235     {
41236         e.preventDefault();
41237         
41238         this.indicator = Math.min(this.files.length, this.indicator + 1);
41239         
41240         this.update();
41241     }
41242 });
41243 /*
41244  * - LGPL
41245  *
41246  * RadioSet
41247  *
41248  *
41249  */
41250
41251 /**
41252  * @class Roo.bootstrap.form.RadioSet
41253  * @extends Roo.bootstrap.form.Input
41254  * @children Roo.bootstrap.form.Radio
41255  * Bootstrap RadioSet class
41256  * @cfg {String} indicatorpos (left|right) default left
41257  * @cfg {Boolean} inline (true|false) inline the element (default true)
41258  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
41259  * @constructor
41260  * Create a new RadioSet
41261  * @param {Object} config The config object
41262  */
41263
41264 Roo.bootstrap.form.RadioSet = function(config){
41265     
41266     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
41267     
41268     this.radioes = [];
41269     
41270     Roo.bootstrap.form.RadioSet.register(this);
41271     
41272     this.addEvents({
41273         /**
41274         * @event check
41275         * Fires when the element is checked or unchecked.
41276         * @param {Roo.bootstrap.form.RadioSet} this This radio
41277         * @param {Roo.bootstrap.form.Radio} item The checked item
41278         */
41279        check : true,
41280        /**
41281         * @event click
41282         * Fires when the element is click.
41283         * @param {Roo.bootstrap.form.RadioSet} this This radio set
41284         * @param {Roo.bootstrap.form.Radio} item The checked item
41285         * @param {Roo.EventObject} e The event object
41286         */
41287        click : true
41288     });
41289     
41290 };
41291
41292 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
41293
41294     radioes : false,
41295     
41296     inline : true,
41297     
41298     weight : '',
41299     
41300     indicatorpos : 'left',
41301     
41302     getAutoCreate : function()
41303     {
41304         var label = {
41305             tag : 'label',
41306             cls : 'roo-radio-set-label',
41307             cn : [
41308                 {
41309                     tag : 'span',
41310                     html : this.fieldLabel
41311                 }
41312             ]
41313         };
41314         if (Roo.bootstrap.version == 3) {
41315             
41316             
41317             if(this.indicatorpos == 'left'){
41318                 label.cn.unshift({
41319                     tag : 'i',
41320                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
41321                     tooltip : 'This field is required'
41322                 });
41323             } else {
41324                 label.cn.push({
41325                     tag : 'i',
41326                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
41327                     tooltip : 'This field is required'
41328                 });
41329             }
41330         }
41331         var items = {
41332             tag : 'div',
41333             cls : 'roo-radio-set-items'
41334         };
41335         
41336         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
41337         
41338         if (align === 'left' && this.fieldLabel.length) {
41339             
41340             items = {
41341                 cls : "roo-radio-set-right", 
41342                 cn: [
41343                     items
41344                 ]
41345             };
41346             
41347             if(this.labelWidth > 12){
41348                 label.style = "width: " + this.labelWidth + 'px';
41349             }
41350             
41351             if(this.labelWidth < 13 && this.labelmd == 0){
41352                 this.labelmd = this.labelWidth;
41353             }
41354             
41355             if(this.labellg > 0){
41356                 label.cls += ' col-lg-' + this.labellg;
41357                 items.cls += ' col-lg-' + (12 - this.labellg);
41358             }
41359             
41360             if(this.labelmd > 0){
41361                 label.cls += ' col-md-' + this.labelmd;
41362                 items.cls += ' col-md-' + (12 - this.labelmd);
41363             }
41364             
41365             if(this.labelsm > 0){
41366                 label.cls += ' col-sm-' + this.labelsm;
41367                 items.cls += ' col-sm-' + (12 - this.labelsm);
41368             }
41369             
41370             if(this.labelxs > 0){
41371                 label.cls += ' col-xs-' + this.labelxs;
41372                 items.cls += ' col-xs-' + (12 - this.labelxs);
41373             }
41374         }
41375         
41376         var cfg = {
41377             tag : 'div',
41378             cls : 'roo-radio-set',
41379             cn : [
41380                 {
41381                     tag : 'input',
41382                     cls : 'roo-radio-set-input',
41383                     type : 'hidden',
41384                     name : this.name,
41385                     value : this.value ? this.value :  ''
41386                 },
41387                 label,
41388                 items
41389             ]
41390         };
41391         
41392         if(this.weight.length){
41393             cfg.cls += ' roo-radio-' + this.weight;
41394         }
41395         
41396         if(this.inline) {
41397             cfg.cls += ' roo-radio-set-inline';
41398         }
41399         
41400         var settings=this;
41401         ['xs','sm','md','lg'].map(function(size){
41402             if (settings[size]) {
41403                 cfg.cls += ' col-' + size + '-' + settings[size];
41404             }
41405         });
41406         
41407         return cfg;
41408         
41409     },
41410
41411     initEvents : function()
41412     {
41413         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
41414         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
41415         
41416         if(!this.fieldLabel.length){
41417             this.labelEl.hide();
41418         }
41419         
41420         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
41421         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
41422         
41423         this.indicator = this.indicatorEl();
41424         
41425         if(this.indicator){
41426             this.indicator.addClass('invisible');
41427         }
41428         
41429         this.originalValue = this.getValue();
41430         
41431     },
41432     
41433     inputEl: function ()
41434     {
41435         return this.el.select('.roo-radio-set-input', true).first();
41436     },
41437     
41438     getChildContainer : function()
41439     {
41440         return this.itemsEl;
41441     },
41442     
41443     register : function(item)
41444     {
41445         this.radioes.push(item);
41446         
41447     },
41448     
41449     validate : function()
41450     {   
41451         if(this.getVisibilityEl().hasClass('hidden')){
41452             return true;
41453         }
41454         
41455         var valid = false;
41456         
41457         Roo.each(this.radioes, function(i){
41458             if(!i.checked){
41459                 return;
41460             }
41461             
41462             valid = true;
41463             return false;
41464         });
41465         
41466         if(this.allowBlank) {
41467             return true;
41468         }
41469         
41470         if(this.disabled || valid){
41471             this.markValid();
41472             return true;
41473         }
41474         
41475         this.markInvalid();
41476         return false;
41477         
41478     },
41479     
41480     markValid : function()
41481     {
41482         if(this.labelEl.isVisible(true) && this.indicatorEl()){
41483             this.indicatorEl().removeClass('visible');
41484             this.indicatorEl().addClass('invisible');
41485         }
41486         
41487         
41488         if (Roo.bootstrap.version == 3) {
41489             this.el.removeClass([this.invalidClass, this.validClass]);
41490             this.el.addClass(this.validClass);
41491         } else {
41492             this.el.removeClass(['is-invalid','is-valid']);
41493             this.el.addClass(['is-valid']);
41494         }
41495         this.fireEvent('valid', this);
41496     },
41497     
41498     markInvalid : function(msg)
41499     {
41500         if(this.allowBlank || this.disabled){
41501             return;
41502         }
41503         
41504         if(this.labelEl.isVisible(true) && this.indicatorEl()){
41505             this.indicatorEl().removeClass('invisible');
41506             this.indicatorEl().addClass('visible');
41507         }
41508         if (Roo.bootstrap.version == 3) {
41509             this.el.removeClass([this.invalidClass, this.validClass]);
41510             this.el.addClass(this.invalidClass);
41511         } else {
41512             this.el.removeClass(['is-invalid','is-valid']);
41513             this.el.addClass(['is-invalid']);
41514         }
41515         
41516         this.fireEvent('invalid', this, msg);
41517         
41518     },
41519     
41520     setValue : function(v, suppressEvent)
41521     {   
41522         if(this.value === v){
41523             return;
41524         }
41525         
41526         this.value = v;
41527         
41528         if(this.rendered){
41529             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41530         }
41531         
41532         Roo.each(this.radioes, function(i){
41533             i.checked = false;
41534             i.el.removeClass('checked');
41535         });
41536         
41537         Roo.each(this.radioes, function(i){
41538             
41539             if(i.value === v || i.value.toString() === v.toString()){
41540                 i.checked = true;
41541                 i.el.addClass('checked');
41542                 
41543                 if(suppressEvent !== true){
41544                     this.fireEvent('check', this, i);
41545                 }
41546                 
41547                 return false;
41548             }
41549             
41550         }, this);
41551         
41552         this.validate();
41553     },
41554     
41555     clearInvalid : function(){
41556         
41557         if(!this.el || this.preventMark){
41558             return;
41559         }
41560         
41561         this.el.removeClass([this.invalidClass]);
41562         
41563         this.fireEvent('valid', this);
41564     }
41565     
41566 });
41567
41568 Roo.apply(Roo.bootstrap.form.RadioSet, {
41569     
41570     groups: {},
41571     
41572     register : function(set)
41573     {
41574         this.groups[set.name] = set;
41575     },
41576     
41577     get: function(name) 
41578     {
41579         if (typeof(this.groups[name]) == 'undefined') {
41580             return false;
41581         }
41582         
41583         return this.groups[name] ;
41584     }
41585     
41586 });
41587 /*
41588  * Based on:
41589  * Ext JS Library 1.1.1
41590  * Copyright(c) 2006-2007, Ext JS, LLC.
41591  *
41592  * Originally Released Under LGPL - original licence link has changed is not relivant.
41593  *
41594  * Fork - LGPL
41595  * <script type="text/javascript">
41596  */
41597
41598
41599 /**
41600  * @class Roo.bootstrap.SplitBar
41601  * @extends Roo.util.Observable
41602  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
41603  * <br><br>
41604  * Usage:
41605  * <pre><code>
41606 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
41607                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
41608 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
41609 split.minSize = 100;
41610 split.maxSize = 600;
41611 split.animate = true;
41612 split.on('moved', splitterMoved);
41613 </code></pre>
41614  * @constructor
41615  * Create a new SplitBar
41616  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
41617  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
41618  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
41619  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
41620                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
41621                         position of the SplitBar).
41622  */
41623 Roo.bootstrap.SplitBar = function(cfg){
41624     
41625     /** @private */
41626     
41627     //{
41628     //  dragElement : elm
41629     //  resizingElement: el,
41630         // optional..
41631     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
41632     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
41633         // existingProxy ???
41634     //}
41635     
41636     this.el = Roo.get(cfg.dragElement, true);
41637     this.el.dom.unselectable = "on";
41638     /** @private */
41639     this.resizingEl = Roo.get(cfg.resizingElement, true);
41640
41641     /**
41642      * @private
41643      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
41644      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
41645      * @type Number
41646      */
41647     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
41648     
41649     /**
41650      * The minimum size of the resizing element. (Defaults to 0)
41651      * @type Number
41652      */
41653     this.minSize = 0;
41654     
41655     /**
41656      * The maximum size of the resizing element. (Defaults to 2000)
41657      * @type Number
41658      */
41659     this.maxSize = 2000;
41660     
41661     /**
41662      * Whether to animate the transition to the new size
41663      * @type Boolean
41664      */
41665     this.animate = false;
41666     
41667     /**
41668      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
41669      * @type Boolean
41670      */
41671     this.useShim = false;
41672     
41673     /** @private */
41674     this.shim = null;
41675     
41676     if(!cfg.existingProxy){
41677         /** @private */
41678         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
41679     }else{
41680         this.proxy = Roo.get(cfg.existingProxy).dom;
41681     }
41682     /** @private */
41683     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
41684     
41685     /** @private */
41686     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
41687     
41688     /** @private */
41689     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
41690     
41691     /** @private */
41692     this.dragSpecs = {};
41693     
41694     /**
41695      * @private The adapter to use to positon and resize elements
41696      */
41697     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41698     this.adapter.init(this);
41699     
41700     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41701         /** @private */
41702         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
41703         this.el.addClass("roo-splitbar-h");
41704     }else{
41705         /** @private */
41706         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
41707         this.el.addClass("roo-splitbar-v");
41708     }
41709     
41710     this.addEvents({
41711         /**
41712          * @event resize
41713          * Fires when the splitter is moved (alias for {@link #event-moved})
41714          * @param {Roo.bootstrap.SplitBar} this
41715          * @param {Number} newSize the new width or height
41716          */
41717         "resize" : true,
41718         /**
41719          * @event moved
41720          * Fires when the splitter is moved
41721          * @param {Roo.bootstrap.SplitBar} this
41722          * @param {Number} newSize the new width or height
41723          */
41724         "moved" : true,
41725         /**
41726          * @event beforeresize
41727          * Fires before the splitter is dragged
41728          * @param {Roo.bootstrap.SplitBar} this
41729          */
41730         "beforeresize" : true,
41731
41732         "beforeapply" : true
41733     });
41734
41735     Roo.util.Observable.call(this);
41736 };
41737
41738 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
41739     onStartProxyDrag : function(x, y){
41740         this.fireEvent("beforeresize", this);
41741         if(!this.overlay){
41742             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
41743             o.unselectable();
41744             o.enableDisplayMode("block");
41745             // all splitbars share the same overlay
41746             Roo.bootstrap.SplitBar.prototype.overlay = o;
41747         }
41748         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
41749         this.overlay.show();
41750         Roo.get(this.proxy).setDisplayed("block");
41751         var size = this.adapter.getElementSize(this);
41752         this.activeMinSize = this.getMinimumSize();;
41753         this.activeMaxSize = this.getMaximumSize();;
41754         var c1 = size - this.activeMinSize;
41755         var c2 = Math.max(this.activeMaxSize - size, 0);
41756         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41757             this.dd.resetConstraints();
41758             this.dd.setXConstraint(
41759                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
41760                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
41761             );
41762             this.dd.setYConstraint(0, 0);
41763         }else{
41764             this.dd.resetConstraints();
41765             this.dd.setXConstraint(0, 0);
41766             this.dd.setYConstraint(
41767                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
41768                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
41769             );
41770          }
41771         this.dragSpecs.startSize = size;
41772         this.dragSpecs.startPoint = [x, y];
41773         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
41774     },
41775     
41776     /** 
41777      * @private Called after the drag operation by the DDProxy
41778      */
41779     onEndProxyDrag : function(e){
41780         Roo.get(this.proxy).setDisplayed(false);
41781         var endPoint = Roo.lib.Event.getXY(e);
41782         if(this.overlay){
41783             this.overlay.hide();
41784         }
41785         var newSize;
41786         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41787             newSize = this.dragSpecs.startSize + 
41788                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
41789                     endPoint[0] - this.dragSpecs.startPoint[0] :
41790                     this.dragSpecs.startPoint[0] - endPoint[0]
41791                 );
41792         }else{
41793             newSize = this.dragSpecs.startSize + 
41794                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
41795                     endPoint[1] - this.dragSpecs.startPoint[1] :
41796                     this.dragSpecs.startPoint[1] - endPoint[1]
41797                 );
41798         }
41799         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
41800         if(newSize != this.dragSpecs.startSize){
41801             if(this.fireEvent('beforeapply', this, newSize) !== false){
41802                 this.adapter.setElementSize(this, newSize);
41803                 this.fireEvent("moved", this, newSize);
41804                 this.fireEvent("resize", this, newSize);
41805             }
41806         }
41807     },
41808     
41809     /**
41810      * Get the adapter this SplitBar uses
41811      * @return The adapter object
41812      */
41813     getAdapter : function(){
41814         return this.adapter;
41815     },
41816     
41817     /**
41818      * Set the adapter this SplitBar uses
41819      * @param {Object} adapter A SplitBar adapter object
41820      */
41821     setAdapter : function(adapter){
41822         this.adapter = adapter;
41823         this.adapter.init(this);
41824     },
41825     
41826     /**
41827      * Gets the minimum size for the resizing element
41828      * @return {Number} The minimum size
41829      */
41830     getMinimumSize : function(){
41831         return this.minSize;
41832     },
41833     
41834     /**
41835      * Sets the minimum size for the resizing element
41836      * @param {Number} minSize The minimum size
41837      */
41838     setMinimumSize : function(minSize){
41839         this.minSize = minSize;
41840     },
41841     
41842     /**
41843      * Gets the maximum size for the resizing element
41844      * @return {Number} The maximum size
41845      */
41846     getMaximumSize : function(){
41847         return this.maxSize;
41848     },
41849     
41850     /**
41851      * Sets the maximum size for the resizing element
41852      * @param {Number} maxSize The maximum size
41853      */
41854     setMaximumSize : function(maxSize){
41855         this.maxSize = maxSize;
41856     },
41857     
41858     /**
41859      * Sets the initialize size for the resizing element
41860      * @param {Number} size The initial size
41861      */
41862     setCurrentSize : function(size){
41863         var oldAnimate = this.animate;
41864         this.animate = false;
41865         this.adapter.setElementSize(this, size);
41866         this.animate = oldAnimate;
41867     },
41868     
41869     /**
41870      * Destroy this splitbar. 
41871      * @param {Boolean} removeEl True to remove the element
41872      */
41873     destroy : function(removeEl){
41874         if(this.shim){
41875             this.shim.remove();
41876         }
41877         this.dd.unreg();
41878         this.proxy.parentNode.removeChild(this.proxy);
41879         if(removeEl){
41880             this.el.remove();
41881         }
41882     }
41883 });
41884
41885 /**
41886  * @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.
41887  */
41888 Roo.bootstrap.SplitBar.createProxy = function(dir){
41889     var proxy = new Roo.Element(document.createElement("div"));
41890     proxy.unselectable();
41891     var cls = 'roo-splitbar-proxy';
41892     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
41893     document.body.appendChild(proxy.dom);
41894     return proxy.dom;
41895 };
41896
41897 /** 
41898  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
41899  * Default Adapter. It assumes the splitter and resizing element are not positioned
41900  * elements and only gets/sets the width of the element. Generally used for table based layouts.
41901  */
41902 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
41903 };
41904
41905 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
41906     // do nothing for now
41907     init : function(s){
41908     
41909     },
41910     /**
41911      * Called before drag operations to get the current size of the resizing element. 
41912      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41913      */
41914      getElementSize : function(s){
41915         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41916             return s.resizingEl.getWidth();
41917         }else{
41918             return s.resizingEl.getHeight();
41919         }
41920     },
41921     
41922     /**
41923      * Called after drag operations to set the size of the resizing element.
41924      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41925      * @param {Number} newSize The new size to set
41926      * @param {Function} onComplete A function to be invoked when resizing is complete
41927      */
41928     setElementSize : function(s, newSize, onComplete){
41929         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41930             if(!s.animate){
41931                 s.resizingEl.setWidth(newSize);
41932                 if(onComplete){
41933                     onComplete(s, newSize);
41934                 }
41935             }else{
41936                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
41937             }
41938         }else{
41939             
41940             if(!s.animate){
41941                 s.resizingEl.setHeight(newSize);
41942                 if(onComplete){
41943                     onComplete(s, newSize);
41944                 }
41945             }else{
41946                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
41947             }
41948         }
41949     }
41950 };
41951
41952 /** 
41953  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
41954  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
41955  * Adapter that  moves the splitter element to align with the resized sizing element. 
41956  * Used with an absolute positioned SplitBar.
41957  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
41958  * document.body, make sure you assign an id to the body element.
41959  */
41960 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
41961     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41962     this.container = Roo.get(container);
41963 };
41964
41965 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
41966     init : function(s){
41967         this.basic.init(s);
41968     },
41969     
41970     getElementSize : function(s){
41971         return this.basic.getElementSize(s);
41972     },
41973     
41974     setElementSize : function(s, newSize, onComplete){
41975         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
41976     },
41977     
41978     moveSplitter : function(s){
41979         var yes = Roo.bootstrap.SplitBar;
41980         switch(s.placement){
41981             case yes.LEFT:
41982                 s.el.setX(s.resizingEl.getRight());
41983                 break;
41984             case yes.RIGHT:
41985                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
41986                 break;
41987             case yes.TOP:
41988                 s.el.setY(s.resizingEl.getBottom());
41989                 break;
41990             case yes.BOTTOM:
41991                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
41992                 break;
41993         }
41994     }
41995 };
41996
41997 /**
41998  * Orientation constant - Create a vertical SplitBar
41999  * @static
42000  * @type Number
42001  */
42002 Roo.bootstrap.SplitBar.VERTICAL = 1;
42003
42004 /**
42005  * Orientation constant - Create a horizontal SplitBar
42006  * @static
42007  * @type Number
42008  */
42009 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42010
42011 /**
42012  * Placement constant - The resizing element is to the left of the splitter element
42013  * @static
42014  * @type Number
42015  */
42016 Roo.bootstrap.SplitBar.LEFT = 1;
42017
42018 /**
42019  * Placement constant - The resizing element is to the right of the splitter element
42020  * @static
42021  * @type Number
42022  */
42023 Roo.bootstrap.SplitBar.RIGHT = 2;
42024
42025 /**
42026  * Placement constant - The resizing element is positioned above the splitter element
42027  * @static
42028  * @type Number
42029  */
42030 Roo.bootstrap.SplitBar.TOP = 3;
42031
42032 /**
42033  * Placement constant - The resizing element is positioned under splitter element
42034  * @static
42035  * @type Number
42036  */
42037 Roo.bootstrap.SplitBar.BOTTOM = 4;
42038 /*
42039  * Based on:
42040  * Ext JS Library 1.1.1
42041  * Copyright(c) 2006-2007, Ext JS, LLC.
42042  *
42043  * Originally Released Under LGPL - original licence link has changed is not relivant.
42044  *
42045  * Fork - LGPL
42046  * <script type="text/javascript">
42047  */
42048
42049 /**
42050  * @class Roo.bootstrap.layout.Manager
42051  * @extends Roo.bootstrap.Component
42052  * @abstract
42053  * Base class for layout managers.
42054  */
42055 Roo.bootstrap.layout.Manager = function(config)
42056 {
42057     this.monitorWindowResize = true; // do this before we apply configuration.
42058     
42059     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42060
42061
42062
42063
42064
42065     /** false to disable window resize monitoring @type Boolean */
42066     
42067     this.regions = {};
42068     this.addEvents({
42069         /**
42070          * @event layout
42071          * Fires when a layout is performed.
42072          * @param {Roo.LayoutManager} this
42073          */
42074         "layout" : true,
42075         /**
42076          * @event regionresized
42077          * Fires when the user resizes a region.
42078          * @param {Roo.LayoutRegion} region The resized region
42079          * @param {Number} newSize The new size (width for east/west, height for north/south)
42080          */
42081         "regionresized" : true,
42082         /**
42083          * @event regioncollapsed
42084          * Fires when a region is collapsed.
42085          * @param {Roo.LayoutRegion} region The collapsed region
42086          */
42087         "regioncollapsed" : true,
42088         /**
42089          * @event regionexpanded
42090          * Fires when a region is expanded.
42091          * @param {Roo.LayoutRegion} region The expanded region
42092          */
42093         "regionexpanded" : true
42094     });
42095     this.updating = false;
42096
42097     if (config.el) {
42098         this.el = Roo.get(config.el);
42099         this.initEvents();
42100     }
42101
42102 };
42103
42104 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42105
42106
42107     regions : null,
42108
42109     monitorWindowResize : true,
42110
42111
42112     updating : false,
42113
42114
42115     onRender : function(ct, position)
42116     {
42117         if(!this.el){
42118             this.el = Roo.get(ct);
42119             this.initEvents();
42120         }
42121         //this.fireEvent('render',this);
42122     },
42123
42124
42125     initEvents: function()
42126     {
42127
42128
42129         // ie scrollbar fix
42130         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42131             document.body.scroll = "no";
42132         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42133             this.el.position('relative');
42134         }
42135         this.id = this.el.id;
42136         this.el.addClass("roo-layout-container");
42137         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42138         if(this.el.dom != document.body ) {
42139             this.el.on('resize', this.layout,this);
42140             this.el.on('show', this.layout,this);
42141         }
42142
42143     },
42144
42145     /**
42146      * Returns true if this layout is currently being updated
42147      * @return {Boolean}
42148      */
42149     isUpdating : function(){
42150         return this.updating;
42151     },
42152
42153     /**
42154      * Suspend the LayoutManager from doing auto-layouts while
42155      * making multiple add or remove calls
42156      */
42157     beginUpdate : function(){
42158         this.updating = true;
42159     },
42160
42161     /**
42162      * Restore auto-layouts and optionally disable the manager from performing a layout
42163      * @param {Boolean} noLayout true to disable a layout update
42164      */
42165     endUpdate : function(noLayout){
42166         this.updating = false;
42167         if(!noLayout){
42168             this.layout();
42169         }
42170     },
42171
42172     layout: function(){
42173         // abstract...
42174     },
42175
42176     onRegionResized : function(region, newSize){
42177         this.fireEvent("regionresized", region, newSize);
42178         this.layout();
42179     },
42180
42181     onRegionCollapsed : function(region){
42182         this.fireEvent("regioncollapsed", region);
42183     },
42184
42185     onRegionExpanded : function(region){
42186         this.fireEvent("regionexpanded", region);
42187     },
42188
42189     /**
42190      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42191      * performs box-model adjustments.
42192      * @return {Object} The size as an object {width: (the width), height: (the height)}
42193      */
42194     getViewSize : function()
42195     {
42196         var size;
42197         if(this.el.dom != document.body){
42198             size = this.el.getSize();
42199         }else{
42200             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42201         }
42202         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42203         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42204         return size;
42205     },
42206
42207     /**
42208      * Returns the Element this layout is bound to.
42209      * @return {Roo.Element}
42210      */
42211     getEl : function(){
42212         return this.el;
42213     },
42214
42215     /**
42216      * Returns the specified region.
42217      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42218      * @return {Roo.LayoutRegion}
42219      */
42220     getRegion : function(target){
42221         return this.regions[target.toLowerCase()];
42222     },
42223
42224     onWindowResize : function(){
42225         if(this.monitorWindowResize){
42226             this.layout();
42227         }
42228     }
42229 });
42230 /*
42231  * Based on:
42232  * Ext JS Library 1.1.1
42233  * Copyright(c) 2006-2007, Ext JS, LLC.
42234  *
42235  * Originally Released Under LGPL - original licence link has changed is not relivant.
42236  *
42237  * Fork - LGPL
42238  * <script type="text/javascript">
42239  */
42240 /**
42241  * @class Roo.bootstrap.layout.Border
42242  * @extends Roo.bootstrap.layout.Manager
42243  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
42244  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
42245  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42246  * please see: examples/bootstrap/nested.html<br><br>
42247  
42248 <b>The container the layout is rendered into can be either the body element or any other element.
42249 If it is not the body element, the container needs to either be an absolute positioned element,
42250 or you will need to add "position:relative" to the css of the container.  You will also need to specify
42251 the container size if it is not the body element.</b>
42252
42253 * @constructor
42254 * Create a new Border
42255 * @param {Object} config Configuration options
42256  */
42257 Roo.bootstrap.layout.Border = function(config){
42258     config = config || {};
42259     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
42260     
42261     
42262     
42263     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42264         if(config[region]){
42265             config[region].region = region;
42266             this.addRegion(config[region]);
42267         }
42268     },this);
42269     
42270 };
42271
42272 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
42273
42274 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
42275     
42276         /**
42277          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
42278          */
42279         /**
42280          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
42281          */
42282         /**
42283          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
42284          */
42285         /**
42286          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
42287          */
42288         /**
42289          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
42290          */
42291         
42292         
42293         
42294         
42295     parent : false, // this might point to a 'nest' or a ???
42296     
42297     /**
42298      * Creates and adds a new region if it doesn't already exist.
42299      * @param {String} target The target region key (north, south, east, west or center).
42300      * @param {Object} config The regions config object
42301      * @return {BorderLayoutRegion} The new region
42302      */
42303     addRegion : function(config)
42304     {
42305         if(!this.regions[config.region]){
42306             var r = this.factory(config);
42307             this.bindRegion(r);
42308         }
42309         return this.regions[config.region];
42310     },
42311
42312     // private (kinda)
42313     bindRegion : function(r){
42314         this.regions[r.config.region] = r;
42315         
42316         r.on("visibilitychange",    this.layout, this);
42317         r.on("paneladded",          this.layout, this);
42318         r.on("panelremoved",        this.layout, this);
42319         r.on("invalidated",         this.layout, this);
42320         r.on("resized",             this.onRegionResized, this);
42321         r.on("collapsed",           this.onRegionCollapsed, this);
42322         r.on("expanded",            this.onRegionExpanded, this);
42323     },
42324
42325     /**
42326      * Performs a layout update.
42327      */
42328     layout : function()
42329     {
42330         if(this.updating) {
42331             return;
42332         }
42333         
42334         // render all the rebions if they have not been done alreayd?
42335         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42336             if(this.regions[region] && !this.regions[region].bodyEl){
42337                 this.regions[region].onRender(this.el)
42338             }
42339         },this);
42340         
42341         var size = this.getViewSize();
42342         var w = size.width;
42343         var h = size.height;
42344         var centerW = w;
42345         var centerH = h;
42346         var centerY = 0;
42347         var centerX = 0;
42348         //var x = 0, y = 0;
42349
42350         var rs = this.regions;
42351         var north = rs["north"];
42352         var south = rs["south"]; 
42353         var west = rs["west"];
42354         var east = rs["east"];
42355         var center = rs["center"];
42356         //if(this.hideOnLayout){ // not supported anymore
42357             //c.el.setStyle("display", "none");
42358         //}
42359         if(north && north.isVisible()){
42360             var b = north.getBox();
42361             var m = north.getMargins();
42362             b.width = w - (m.left+m.right);
42363             b.x = m.left;
42364             b.y = m.top;
42365             centerY = b.height + b.y + m.bottom;
42366             centerH -= centerY;
42367             north.updateBox(this.safeBox(b));
42368         }
42369         if(south && south.isVisible()){
42370             var b = south.getBox();
42371             var m = south.getMargins();
42372             b.width = w - (m.left+m.right);
42373             b.x = m.left;
42374             var totalHeight = (b.height + m.top + m.bottom);
42375             b.y = h - totalHeight + m.top;
42376             centerH -= totalHeight;
42377             south.updateBox(this.safeBox(b));
42378         }
42379         if(west && west.isVisible()){
42380             var b = west.getBox();
42381             var m = west.getMargins();
42382             b.height = centerH - (m.top+m.bottom);
42383             b.x = m.left;
42384             b.y = centerY + m.top;
42385             var totalWidth = (b.width + m.left + m.right);
42386             centerX += totalWidth;
42387             centerW -= totalWidth;
42388             west.updateBox(this.safeBox(b));
42389         }
42390         if(east && east.isVisible()){
42391             var b = east.getBox();
42392             var m = east.getMargins();
42393             b.height = centerH - (m.top+m.bottom);
42394             var totalWidth = (b.width + m.left + m.right);
42395             b.x = w - totalWidth + m.left;
42396             b.y = centerY + m.top;
42397             centerW -= totalWidth;
42398             east.updateBox(this.safeBox(b));
42399         }
42400         if(center){
42401             var m = center.getMargins();
42402             var centerBox = {
42403                 x: centerX + m.left,
42404                 y: centerY + m.top,
42405                 width: centerW - (m.left+m.right),
42406                 height: centerH - (m.top+m.bottom)
42407             };
42408             //if(this.hideOnLayout){
42409                 //center.el.setStyle("display", "block");
42410             //}
42411             center.updateBox(this.safeBox(centerBox));
42412         }
42413         this.el.repaint();
42414         this.fireEvent("layout", this);
42415     },
42416
42417     // private
42418     safeBox : function(box){
42419         box.width = Math.max(0, box.width);
42420         box.height = Math.max(0, box.height);
42421         return box;
42422     },
42423
42424     /**
42425      * Adds a ContentPanel (or subclass) to this layout.
42426      * @param {String} target The target region key (north, south, east, west or center).
42427      * @param {Roo.ContentPanel} panel The panel to add
42428      * @return {Roo.ContentPanel} The added panel
42429      */
42430     add : function(target, panel){
42431          
42432         target = target.toLowerCase();
42433         return this.regions[target].add(panel);
42434     },
42435
42436     /**
42437      * Remove a ContentPanel (or subclass) to this layout.
42438      * @param {String} target The target region key (north, south, east, west or center).
42439      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42440      * @return {Roo.ContentPanel} The removed panel
42441      */
42442     remove : function(target, panel){
42443         target = target.toLowerCase();
42444         return this.regions[target].remove(panel);
42445     },
42446
42447     /**
42448      * Searches all regions for a panel with the specified id
42449      * @param {String} panelId
42450      * @return {Roo.ContentPanel} The panel or null if it wasn't found
42451      */
42452     findPanel : function(panelId){
42453         var rs = this.regions;
42454         for(var target in rs){
42455             if(typeof rs[target] != "function"){
42456                 var p = rs[target].getPanel(panelId);
42457                 if(p){
42458                     return p;
42459                 }
42460             }
42461         }
42462         return null;
42463     },
42464
42465     /**
42466      * Searches all regions for a panel with the specified id and activates (shows) it.
42467      * @param {String/ContentPanel} panelId The panels id or the panel itself
42468      * @return {Roo.ContentPanel} The shown panel or null
42469      */
42470     showPanel : function(panelId) {
42471       var rs = this.regions;
42472       for(var target in rs){
42473          var r = rs[target];
42474          if(typeof r != "function"){
42475             if(r.hasPanel(panelId)){
42476                return r.showPanel(panelId);
42477             }
42478          }
42479       }
42480       return null;
42481    },
42482
42483    /**
42484      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42485      * @param {Roo.state.Provider} provider (optional) An alternate state provider
42486      */
42487    /*
42488     restoreState : function(provider){
42489         if(!provider){
42490             provider = Roo.state.Manager;
42491         }
42492         var sm = new Roo.LayoutStateManager();
42493         sm.init(this, provider);
42494     },
42495 */
42496  
42497  
42498     /**
42499      * Adds a xtype elements to the layout.
42500      * <pre><code>
42501
42502 layout.addxtype({
42503        xtype : 'ContentPanel',
42504        region: 'west',
42505        items: [ .... ]
42506    }
42507 );
42508
42509 layout.addxtype({
42510         xtype : 'NestedLayoutPanel',
42511         region: 'west',
42512         layout: {
42513            center: { },
42514            west: { }   
42515         },
42516         items : [ ... list of content panels or nested layout panels.. ]
42517    }
42518 );
42519 </code></pre>
42520      * @param {Object} cfg Xtype definition of item to add.
42521      */
42522     addxtype : function(cfg)
42523     {
42524         // basically accepts a pannel...
42525         // can accept a layout region..!?!?
42526         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
42527         
42528         
42529         // theory?  children can only be panels??
42530         
42531         //if (!cfg.xtype.match(/Panel$/)) {
42532         //    return false;
42533         //}
42534         var ret = false;
42535         
42536         if (typeof(cfg.region) == 'undefined') {
42537             Roo.log("Failed to add Panel, region was not set");
42538             Roo.log(cfg);
42539             return false;
42540         }
42541         var region = cfg.region;
42542         delete cfg.region;
42543         
42544           
42545         var xitems = [];
42546         if (cfg.items) {
42547             xitems = cfg.items;
42548             delete cfg.items;
42549         }
42550         var nb = false;
42551         
42552         if ( region == 'center') {
42553             Roo.log("Center: " + cfg.title);
42554         }
42555         
42556         
42557         switch(cfg.xtype) 
42558         {
42559             case 'Content':  // ContentPanel (el, cfg)
42560             case 'Scroll':  // ContentPanel (el, cfg)
42561             case 'View': 
42562                 cfg.autoCreate = cfg.autoCreate || true;
42563                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42564                 //} else {
42565                 //    var el = this.el.createChild();
42566                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
42567                 //}
42568                 
42569                 this.add(region, ret);
42570                 break;
42571             
42572             /*
42573             case 'TreePanel': // our new panel!
42574                 cfg.el = this.el.createChild();
42575                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42576                 this.add(region, ret);
42577                 break;
42578             */
42579             
42580             case 'Nest': 
42581                 // create a new Layout (which is  a Border Layout...
42582                 
42583                 var clayout = cfg.layout;
42584                 clayout.el  = this.el.createChild();
42585                 clayout.items   = clayout.items  || [];
42586                 
42587                 delete cfg.layout;
42588                 
42589                 // replace this exitems with the clayout ones..
42590                 xitems = clayout.items;
42591                  
42592                 // force background off if it's in center...
42593                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
42594                     cfg.background = false;
42595                 }
42596                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
42597                 
42598                 
42599                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42600                 //console.log('adding nested layout panel '  + cfg.toSource());
42601                 this.add(region, ret);
42602                 nb = {}; /// find first...
42603                 break;
42604             
42605             case 'Grid':
42606                 
42607                 // needs grid and region
42608                 
42609                 //var el = this.getRegion(region).el.createChild();
42610                 /*
42611                  *var el = this.el.createChild();
42612                 // create the grid first...
42613                 cfg.grid.container = el;
42614                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
42615                 */
42616                 
42617                 if (region == 'center' && this.active ) {
42618                     cfg.background = false;
42619                 }
42620                 
42621                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42622                 
42623                 this.add(region, ret);
42624                 /*
42625                 if (cfg.background) {
42626                     // render grid on panel activation (if panel background)
42627                     ret.on('activate', function(gp) {
42628                         if (!gp.grid.rendered) {
42629                     //        gp.grid.render(el);
42630                         }
42631                     });
42632                 } else {
42633                   //  cfg.grid.render(el);
42634                 }
42635                 */
42636                 break;
42637            
42638            
42639             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
42640                 // it was the old xcomponent building that caused this before.
42641                 // espeically if border is the top element in the tree.
42642                 ret = this;
42643                 break; 
42644                 
42645                     
42646                 
42647                 
42648                 
42649             default:
42650                 /*
42651                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
42652                     
42653                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42654                     this.add(region, ret);
42655                 } else {
42656                 */
42657                     Roo.log(cfg);
42658                     throw "Can not add '" + cfg.xtype + "' to Border";
42659                     return null;
42660              
42661                                 
42662              
42663         }
42664         this.beginUpdate();
42665         // add children..
42666         var region = '';
42667         var abn = {};
42668         Roo.each(xitems, function(i)  {
42669             region = nb && i.region ? i.region : false;
42670             
42671             var add = ret.addxtype(i);
42672            
42673             if (region) {
42674                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
42675                 if (!i.background) {
42676                     abn[region] = nb[region] ;
42677                 }
42678             }
42679             
42680         });
42681         this.endUpdate();
42682
42683         // make the last non-background panel active..
42684         //if (nb) { Roo.log(abn); }
42685         if (nb) {
42686             
42687             for(var r in abn) {
42688                 region = this.getRegion(r);
42689                 if (region) {
42690                     // tried using nb[r], but it does not work..
42691                      
42692                     region.showPanel(abn[r]);
42693                    
42694                 }
42695             }
42696         }
42697         return ret;
42698         
42699     },
42700     
42701     
42702 // private
42703     factory : function(cfg)
42704     {
42705         
42706         var validRegions = Roo.bootstrap.layout.Border.regions;
42707
42708         var target = cfg.region;
42709         cfg.mgr = this;
42710         
42711         var r = Roo.bootstrap.layout;
42712         Roo.log(target);
42713         switch(target){
42714             case "north":
42715                 return new r.North(cfg);
42716             case "south":
42717                 return new r.South(cfg);
42718             case "east":
42719                 return new r.East(cfg);
42720             case "west":
42721                 return new r.West(cfg);
42722             case "center":
42723                 return new r.Center(cfg);
42724         }
42725         throw 'Layout region "'+target+'" not supported.';
42726     }
42727     
42728     
42729 });
42730  /*
42731  * Based on:
42732  * Ext JS Library 1.1.1
42733  * Copyright(c) 2006-2007, Ext JS, LLC.
42734  *
42735  * Originally Released Under LGPL - original licence link has changed is not relivant.
42736  *
42737  * Fork - LGPL
42738  * <script type="text/javascript">
42739  */
42740  
42741 /**
42742  * @class Roo.bootstrap.layout.Basic
42743  * @extends Roo.util.Observable
42744  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
42745  * and does not have a titlebar, tabs or any other features. All it does is size and position 
42746  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
42747  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
42748  * @cfg {string}   region  the region that it inhabits..
42749  * @cfg {bool}   skipConfig skip config?
42750  * 
42751
42752  */
42753 Roo.bootstrap.layout.Basic = function(config){
42754     
42755     this.mgr = config.mgr;
42756     
42757     this.position = config.region;
42758     
42759     var skipConfig = config.skipConfig;
42760     
42761     this.events = {
42762         /**
42763          * @scope Roo.BasicLayoutRegion
42764          */
42765         
42766         /**
42767          * @event beforeremove
42768          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
42769          * @param {Roo.LayoutRegion} this
42770          * @param {Roo.ContentPanel} panel The panel
42771          * @param {Object} e The cancel event object
42772          */
42773         "beforeremove" : true,
42774         /**
42775          * @event invalidated
42776          * Fires when the layout for this region is changed.
42777          * @param {Roo.LayoutRegion} this
42778          */
42779         "invalidated" : true,
42780         /**
42781          * @event visibilitychange
42782          * Fires when this region is shown or hidden 
42783          * @param {Roo.LayoutRegion} this
42784          * @param {Boolean} visibility true or false
42785          */
42786         "visibilitychange" : true,
42787         /**
42788          * @event paneladded
42789          * Fires when a panel is added. 
42790          * @param {Roo.LayoutRegion} this
42791          * @param {Roo.ContentPanel} panel The panel
42792          */
42793         "paneladded" : true,
42794         /**
42795          * @event panelremoved
42796          * Fires when a panel is removed. 
42797          * @param {Roo.LayoutRegion} this
42798          * @param {Roo.ContentPanel} panel The panel
42799          */
42800         "panelremoved" : true,
42801         /**
42802          * @event beforecollapse
42803          * Fires when this region before collapse.
42804          * @param {Roo.LayoutRegion} this
42805          */
42806         "beforecollapse" : true,
42807         /**
42808          * @event collapsed
42809          * Fires when this region is collapsed.
42810          * @param {Roo.LayoutRegion} this
42811          */
42812         "collapsed" : true,
42813         /**
42814          * @event expanded
42815          * Fires when this region is expanded.
42816          * @param {Roo.LayoutRegion} this
42817          */
42818         "expanded" : true,
42819         /**
42820          * @event slideshow
42821          * Fires when this region is slid into view.
42822          * @param {Roo.LayoutRegion} this
42823          */
42824         "slideshow" : true,
42825         /**
42826          * @event slidehide
42827          * Fires when this region slides out of view. 
42828          * @param {Roo.LayoutRegion} this
42829          */
42830         "slidehide" : true,
42831         /**
42832          * @event panelactivated
42833          * Fires when a panel is activated. 
42834          * @param {Roo.LayoutRegion} this
42835          * @param {Roo.ContentPanel} panel The activated panel
42836          */
42837         "panelactivated" : true,
42838         /**
42839          * @event resized
42840          * Fires when the user resizes this region. 
42841          * @param {Roo.LayoutRegion} this
42842          * @param {Number} newSize The new size (width for east/west, height for north/south)
42843          */
42844         "resized" : true
42845     };
42846     /** A collection of panels in this region. @type Roo.util.MixedCollection */
42847     this.panels = new Roo.util.MixedCollection();
42848     this.panels.getKey = this.getPanelId.createDelegate(this);
42849     this.box = null;
42850     this.activePanel = null;
42851     // ensure listeners are added...
42852     
42853     if (config.listeners || config.events) {
42854         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
42855             listeners : config.listeners || {},
42856             events : config.events || {}
42857         });
42858     }
42859     
42860     if(skipConfig !== true){
42861         this.applyConfig(config);
42862     }
42863 };
42864
42865 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
42866 {
42867     getPanelId : function(p){
42868         return p.getId();
42869     },
42870     
42871     applyConfig : function(config){
42872         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
42873         this.config = config;
42874         
42875     },
42876     
42877     /**
42878      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
42879      * the width, for horizontal (north, south) the height.
42880      * @param {Number} newSize The new width or height
42881      */
42882     resizeTo : function(newSize){
42883         var el = this.el ? this.el :
42884                  (this.activePanel ? this.activePanel.getEl() : null);
42885         if(el){
42886             switch(this.position){
42887                 case "east":
42888                 case "west":
42889                     el.setWidth(newSize);
42890                     this.fireEvent("resized", this, newSize);
42891                 break;
42892                 case "north":
42893                 case "south":
42894                     el.setHeight(newSize);
42895                     this.fireEvent("resized", this, newSize);
42896                 break;                
42897             }
42898         }
42899     },
42900     
42901     getBox : function(){
42902         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
42903     },
42904     
42905     getMargins : function(){
42906         return this.margins;
42907     },
42908     
42909     updateBox : function(box){
42910         this.box = box;
42911         var el = this.activePanel.getEl();
42912         el.dom.style.left = box.x + "px";
42913         el.dom.style.top = box.y + "px";
42914         this.activePanel.setSize(box.width, box.height);
42915     },
42916     
42917     /**
42918      * Returns the container element for this region.
42919      * @return {Roo.Element}
42920      */
42921     getEl : function(){
42922         return this.activePanel;
42923     },
42924     
42925     /**
42926      * Returns true if this region is currently visible.
42927      * @return {Boolean}
42928      */
42929     isVisible : function(){
42930         return this.activePanel ? true : false;
42931     },
42932     
42933     setActivePanel : function(panel){
42934         panel = this.getPanel(panel);
42935         if(this.activePanel && this.activePanel != panel){
42936             this.activePanel.setActiveState(false);
42937             this.activePanel.getEl().setLeftTop(-10000,-10000);
42938         }
42939         this.activePanel = panel;
42940         panel.setActiveState(true);
42941         if(this.box){
42942             panel.setSize(this.box.width, this.box.height);
42943         }
42944         this.fireEvent("panelactivated", this, panel);
42945         this.fireEvent("invalidated");
42946     },
42947     
42948     /**
42949      * Show the specified panel.
42950      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
42951      * @return {Roo.ContentPanel} The shown panel or null
42952      */
42953     showPanel : function(panel){
42954         panel = this.getPanel(panel);
42955         if(panel){
42956             this.setActivePanel(panel);
42957         }
42958         return panel;
42959     },
42960     
42961     /**
42962      * Get the active panel for this region.
42963      * @return {Roo.ContentPanel} The active panel or null
42964      */
42965     getActivePanel : function(){
42966         return this.activePanel;
42967     },
42968     
42969     /**
42970      * Add the passed ContentPanel(s)
42971      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42972      * @return {Roo.ContentPanel} The panel added (if only one was added)
42973      */
42974     add : function(panel){
42975         if(arguments.length > 1){
42976             for(var i = 0, len = arguments.length; i < len; i++) {
42977                 this.add(arguments[i]);
42978             }
42979             return null;
42980         }
42981         if(this.hasPanel(panel)){
42982             this.showPanel(panel);
42983             return panel;
42984         }
42985         var el = panel.getEl();
42986         if(el.dom.parentNode != this.mgr.el.dom){
42987             this.mgr.el.dom.appendChild(el.dom);
42988         }
42989         if(panel.setRegion){
42990             panel.setRegion(this);
42991         }
42992         this.panels.add(panel);
42993         el.setStyle("position", "absolute");
42994         if(!panel.background){
42995             this.setActivePanel(panel);
42996             if(this.config.initialSize && this.panels.getCount()==1){
42997                 this.resizeTo(this.config.initialSize);
42998             }
42999         }
43000         this.fireEvent("paneladded", this, panel);
43001         return panel;
43002     },
43003     
43004     /**
43005      * Returns true if the panel is in this region.
43006      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43007      * @return {Boolean}
43008      */
43009     hasPanel : function(panel){
43010         if(typeof panel == "object"){ // must be panel obj
43011             panel = panel.getId();
43012         }
43013         return this.getPanel(panel) ? true : false;
43014     },
43015     
43016     /**
43017      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43018      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43019      * @param {Boolean} preservePanel Overrides the config preservePanel option
43020      * @return {Roo.ContentPanel} The panel that was removed
43021      */
43022     remove : function(panel, preservePanel){
43023         panel = this.getPanel(panel);
43024         if(!panel){
43025             return null;
43026         }
43027         var e = {};
43028         this.fireEvent("beforeremove", this, panel, e);
43029         if(e.cancel === true){
43030             return null;
43031         }
43032         var panelId = panel.getId();
43033         this.panels.removeKey(panelId);
43034         return panel;
43035     },
43036     
43037     /**
43038      * Returns the panel specified or null if it's not in this region.
43039      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43040      * @return {Roo.ContentPanel}
43041      */
43042     getPanel : function(id){
43043         if(typeof id == "object"){ // must be panel obj
43044             return id;
43045         }
43046         return this.panels.get(id);
43047     },
43048     
43049     /**
43050      * Returns this regions position (north/south/east/west/center).
43051      * @return {String} 
43052      */
43053     getPosition: function(){
43054         return this.position;    
43055     }
43056 });/*
43057  * Based on:
43058  * Ext JS Library 1.1.1
43059  * Copyright(c) 2006-2007, Ext JS, LLC.
43060  *
43061  * Originally Released Under LGPL - original licence link has changed is not relivant.
43062  *
43063  * Fork - LGPL
43064  * <script type="text/javascript">
43065  */
43066  
43067 /**
43068  * @class Roo.bootstrap.layout.Region
43069  * @extends Roo.bootstrap.layout.Basic
43070  * This class represents a region in a layout manager.
43071  
43072  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43073  * @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})
43074  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
43075  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
43076  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
43077  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
43078  * @cfg {String}    title           The title for the region (overrides panel titles)
43079  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
43080  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43081  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
43082  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43083  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
43084  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43085  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
43086  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
43087  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
43088  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
43089
43090  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
43091  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
43092  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
43093  * @cfg {Number}    width           For East/West panels
43094  * @cfg {Number}    height          For North/South panels
43095  * @cfg {Boolean}   split           To show the splitter
43096  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
43097  * 
43098  * @cfg {string}   cls             Extra CSS classes to add to region
43099  * 
43100  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43101  * @cfg {string}   region  the region that it inhabits..
43102  *
43103
43104  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
43105  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
43106
43107  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
43108  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
43109  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
43110  */
43111 Roo.bootstrap.layout.Region = function(config)
43112 {
43113     this.applyConfig(config);
43114
43115     var mgr = config.mgr;
43116     var pos = config.region;
43117     config.skipConfig = true;
43118     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43119     
43120     if (mgr.el) {
43121         this.onRender(mgr.el);   
43122     }
43123      
43124     this.visible = true;
43125     this.collapsed = false;
43126     this.unrendered_panels = [];
43127 };
43128
43129 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43130
43131     position: '', // set by wrapper (eg. north/south etc..)
43132     unrendered_panels : null,  // unrendered panels.
43133     
43134     tabPosition : false,
43135     
43136     mgr: false, // points to 'Border'
43137     
43138     
43139     createBody : function(){
43140         /** This region's body element 
43141         * @type Roo.Element */
43142         this.bodyEl = this.el.createChild({
43143                 tag: "div",
43144                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43145         });
43146     },
43147
43148     onRender: function(ctr, pos)
43149     {
43150         var dh = Roo.DomHelper;
43151         /** This region's container element 
43152         * @type Roo.Element */
43153         this.el = dh.append(ctr.dom, {
43154                 tag: "div",
43155                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43156             }, true);
43157         /** This region's title element 
43158         * @type Roo.Element */
43159     
43160         this.titleEl = dh.append(this.el.dom,  {
43161                 tag: "div",
43162                 unselectable: "on",
43163                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43164                 children:[
43165                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43166                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43167                 ]
43168             }, true);
43169         
43170         this.titleEl.enableDisplayMode();
43171         /** This region's title text element 
43172         * @type HTMLElement */
43173         this.titleTextEl = this.titleEl.dom.firstChild;
43174         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43175         /*
43176         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43177         this.closeBtn.enableDisplayMode();
43178         this.closeBtn.on("click", this.closeClicked, this);
43179         this.closeBtn.hide();
43180     */
43181         this.createBody(this.config);
43182         if(this.config.hideWhenEmpty){
43183             this.hide();
43184             this.on("paneladded", this.validateVisibility, this);
43185             this.on("panelremoved", this.validateVisibility, this);
43186         }
43187         if(this.autoScroll){
43188             this.bodyEl.setStyle("overflow", "auto");
43189         }else{
43190             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
43191         }
43192         //if(c.titlebar !== false){
43193             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
43194                 this.titleEl.hide();
43195             }else{
43196                 this.titleEl.show();
43197                 if(this.config.title){
43198                     this.titleTextEl.innerHTML = this.config.title;
43199                 }
43200             }
43201         //}
43202         if(this.config.collapsed){
43203             this.collapse(true);
43204         }
43205         if(this.config.hidden){
43206             this.hide();
43207         }
43208         
43209         if (this.unrendered_panels && this.unrendered_panels.length) {
43210             for (var i =0;i< this.unrendered_panels.length; i++) {
43211                 this.add(this.unrendered_panels[i]);
43212             }
43213             this.unrendered_panels = null;
43214             
43215         }
43216         
43217     },
43218     
43219     applyConfig : function(c)
43220     {
43221         /*
43222          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
43223             var dh = Roo.DomHelper;
43224             if(c.titlebar !== false){
43225                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
43226                 this.collapseBtn.on("click", this.collapse, this);
43227                 this.collapseBtn.enableDisplayMode();
43228                 /*
43229                 if(c.showPin === true || this.showPin){
43230                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
43231                     this.stickBtn.enableDisplayMode();
43232                     this.stickBtn.on("click", this.expand, this);
43233                     this.stickBtn.hide();
43234                 }
43235                 
43236             }
43237             */
43238             /** This region's collapsed element
43239             * @type Roo.Element */
43240             /*
43241              *
43242             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43243                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43244             ]}, true);
43245             
43246             if(c.floatable !== false){
43247                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43248                this.collapsedEl.on("click", this.collapseClick, this);
43249             }
43250
43251             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43252                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43253                    id: "message", unselectable: "on", style:{"float":"left"}});
43254                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43255              }
43256             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43257             this.expandBtn.on("click", this.expand, this);
43258             
43259         }
43260         
43261         if(this.collapseBtn){
43262             this.collapseBtn.setVisible(c.collapsible == true);
43263         }
43264         
43265         this.cmargins = c.cmargins || this.cmargins ||
43266                          (this.position == "west" || this.position == "east" ?
43267                              {top: 0, left: 2, right:2, bottom: 0} :
43268                              {top: 2, left: 0, right:0, bottom: 2});
43269         */
43270         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43271         
43272         
43273         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
43274         
43275         this.autoScroll = c.autoScroll || false;
43276         
43277         
43278        
43279         
43280         this.duration = c.duration || .30;
43281         this.slideDuration = c.slideDuration || .45;
43282         this.config = c;
43283        
43284     },
43285     /**
43286      * Returns true if this region is currently visible.
43287      * @return {Boolean}
43288      */
43289     isVisible : function(){
43290         return this.visible;
43291     },
43292
43293     /**
43294      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43295      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
43296      */
43297     //setCollapsedTitle : function(title){
43298     //    title = title || "&#160;";
43299      //   if(this.collapsedTitleTextEl){
43300       //      this.collapsedTitleTextEl.innerHTML = title;
43301        // }
43302     //},
43303
43304     getBox : function(){
43305         var b;
43306       //  if(!this.collapsed){
43307             b = this.el.getBox(false, true);
43308        // }else{
43309           //  b = this.collapsedEl.getBox(false, true);
43310         //}
43311         return b;
43312     },
43313
43314     getMargins : function(){
43315         return this.margins;
43316         //return this.collapsed ? this.cmargins : this.margins;
43317     },
43318 /*
43319     highlight : function(){
43320         this.el.addClass("x-layout-panel-dragover");
43321     },
43322
43323     unhighlight : function(){
43324         this.el.removeClass("x-layout-panel-dragover");
43325     },
43326 */
43327     updateBox : function(box)
43328     {
43329         if (!this.bodyEl) {
43330             return; // not rendered yet..
43331         }
43332         
43333         this.box = box;
43334         if(!this.collapsed){
43335             this.el.dom.style.left = box.x + "px";
43336             this.el.dom.style.top = box.y + "px";
43337             this.updateBody(box.width, box.height);
43338         }else{
43339             this.collapsedEl.dom.style.left = box.x + "px";
43340             this.collapsedEl.dom.style.top = box.y + "px";
43341             this.collapsedEl.setSize(box.width, box.height);
43342         }
43343         if(this.tabs){
43344             this.tabs.autoSizeTabs();
43345         }
43346     },
43347
43348     updateBody : function(w, h)
43349     {
43350         if(w !== null){
43351             this.el.setWidth(w);
43352             w -= this.el.getBorderWidth("rl");
43353             if(this.config.adjustments){
43354                 w += this.config.adjustments[0];
43355             }
43356         }
43357         if(h !== null && h > 0){
43358             this.el.setHeight(h);
43359             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43360             h -= this.el.getBorderWidth("tb");
43361             if(this.config.adjustments){
43362                 h += this.config.adjustments[1];
43363             }
43364             this.bodyEl.setHeight(h);
43365             if(this.tabs){
43366                 h = this.tabs.syncHeight(h);
43367             }
43368         }
43369         if(this.panelSize){
43370             w = w !== null ? w : this.panelSize.width;
43371             h = h !== null ? h : this.panelSize.height;
43372         }
43373         if(this.activePanel){
43374             var el = this.activePanel.getEl();
43375             w = w !== null ? w : el.getWidth();
43376             h = h !== null ? h : el.getHeight();
43377             this.panelSize = {width: w, height: h};
43378             this.activePanel.setSize(w, h);
43379         }
43380         if(Roo.isIE && this.tabs){
43381             this.tabs.el.repaint();
43382         }
43383     },
43384
43385     /**
43386      * Returns the container element for this region.
43387      * @return {Roo.Element}
43388      */
43389     getEl : function(){
43390         return this.el;
43391     },
43392
43393     /**
43394      * Hides this region.
43395      */
43396     hide : function(){
43397         //if(!this.collapsed){
43398             this.el.dom.style.left = "-2000px";
43399             this.el.hide();
43400         //}else{
43401          //   this.collapsedEl.dom.style.left = "-2000px";
43402          //   this.collapsedEl.hide();
43403        // }
43404         this.visible = false;
43405         this.fireEvent("visibilitychange", this, false);
43406     },
43407
43408     /**
43409      * Shows this region if it was previously hidden.
43410      */
43411     show : function(){
43412         //if(!this.collapsed){
43413             this.el.show();
43414         //}else{
43415         //    this.collapsedEl.show();
43416        // }
43417         this.visible = true;
43418         this.fireEvent("visibilitychange", this, true);
43419     },
43420 /*
43421     closeClicked : function(){
43422         if(this.activePanel){
43423             this.remove(this.activePanel);
43424         }
43425     },
43426
43427     collapseClick : function(e){
43428         if(this.isSlid){
43429            e.stopPropagation();
43430            this.slideIn();
43431         }else{
43432            e.stopPropagation();
43433            this.slideOut();
43434         }
43435     },
43436 */
43437     /**
43438      * Collapses this region.
43439      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43440      */
43441     /*
43442     collapse : function(skipAnim, skipCheck = false){
43443         if(this.collapsed) {
43444             return;
43445         }
43446         
43447         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
43448             
43449             this.collapsed = true;
43450             if(this.split){
43451                 this.split.el.hide();
43452             }
43453             if(this.config.animate && skipAnim !== true){
43454                 this.fireEvent("invalidated", this);
43455                 this.animateCollapse();
43456             }else{
43457                 this.el.setLocation(-20000,-20000);
43458                 this.el.hide();
43459                 this.collapsedEl.show();
43460                 this.fireEvent("collapsed", this);
43461                 this.fireEvent("invalidated", this);
43462             }
43463         }
43464         
43465     },
43466 */
43467     animateCollapse : function(){
43468         // overridden
43469     },
43470
43471     /**
43472      * Expands this region if it was previously collapsed.
43473      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43474      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43475      */
43476     /*
43477     expand : function(e, skipAnim){
43478         if(e) {
43479             e.stopPropagation();
43480         }
43481         if(!this.collapsed || this.el.hasActiveFx()) {
43482             return;
43483         }
43484         if(this.isSlid){
43485             this.afterSlideIn();
43486             skipAnim = true;
43487         }
43488         this.collapsed = false;
43489         if(this.config.animate && skipAnim !== true){
43490             this.animateExpand();
43491         }else{
43492             this.el.show();
43493             if(this.split){
43494                 this.split.el.show();
43495             }
43496             this.collapsedEl.setLocation(-2000,-2000);
43497             this.collapsedEl.hide();
43498             this.fireEvent("invalidated", this);
43499             this.fireEvent("expanded", this);
43500         }
43501     },
43502 */
43503     animateExpand : function(){
43504         // overridden
43505     },
43506
43507     initTabs : function()
43508     {
43509         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
43510         
43511         var ts = new Roo.bootstrap.panel.Tabs({
43512             el: this.bodyEl.dom,
43513             region : this,
43514             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
43515             disableTooltips: this.config.disableTabTips,
43516             toolbar : this.config.toolbar
43517         });
43518         
43519         if(this.config.hideTabs){
43520             ts.stripWrap.setDisplayed(false);
43521         }
43522         this.tabs = ts;
43523         ts.resizeTabs = this.config.resizeTabs === true;
43524         ts.minTabWidth = this.config.minTabWidth || 40;
43525         ts.maxTabWidth = this.config.maxTabWidth || 250;
43526         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
43527         ts.monitorResize = false;
43528         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
43529         ts.bodyEl.addClass('roo-layout-tabs-body');
43530         this.panels.each(this.initPanelAsTab, this);
43531     },
43532
43533     initPanelAsTab : function(panel){
43534         var ti = this.tabs.addTab(
43535             panel.getEl().id,
43536             panel.getTitle(),
43537             null,
43538             this.config.closeOnTab && panel.isClosable(),
43539             panel.tpl
43540         );
43541         if(panel.tabTip !== undefined){
43542             ti.setTooltip(panel.tabTip);
43543         }
43544         ti.on("activate", function(){
43545               this.setActivePanel(panel);
43546         }, this);
43547         
43548         if(this.config.closeOnTab){
43549             ti.on("beforeclose", function(t, e){
43550                 e.cancel = true;
43551                 this.remove(panel);
43552             }, this);
43553         }
43554         
43555         panel.tabItem = ti;
43556         
43557         return ti;
43558     },
43559
43560     updatePanelTitle : function(panel, title)
43561     {
43562         if(this.activePanel == panel){
43563             this.updateTitle(title);
43564         }
43565         if(this.tabs){
43566             var ti = this.tabs.getTab(panel.getEl().id);
43567             ti.setText(title);
43568             if(panel.tabTip !== undefined){
43569                 ti.setTooltip(panel.tabTip);
43570             }
43571         }
43572     },
43573
43574     updateTitle : function(title){
43575         if(this.titleTextEl && !this.config.title){
43576             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
43577         }
43578     },
43579
43580     setActivePanel : function(panel)
43581     {
43582         panel = this.getPanel(panel);
43583         if(this.activePanel && this.activePanel != panel){
43584             if(this.activePanel.setActiveState(false) === false){
43585                 return;
43586             }
43587         }
43588         this.activePanel = panel;
43589         panel.setActiveState(true);
43590         if(this.panelSize){
43591             panel.setSize(this.panelSize.width, this.panelSize.height);
43592         }
43593         if(this.closeBtn){
43594             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
43595         }
43596         this.updateTitle(panel.getTitle());
43597         if(this.tabs){
43598             this.fireEvent("invalidated", this);
43599         }
43600         this.fireEvent("panelactivated", this, panel);
43601     },
43602
43603     /**
43604      * Shows the specified panel.
43605      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
43606      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
43607      */
43608     showPanel : function(panel)
43609     {
43610         panel = this.getPanel(panel);
43611         if(panel){
43612             if(this.tabs){
43613                 var tab = this.tabs.getTab(panel.getEl().id);
43614                 if(tab.isHidden()){
43615                     this.tabs.unhideTab(tab.id);
43616                 }
43617                 tab.activate();
43618             }else{
43619                 this.setActivePanel(panel);
43620             }
43621         }
43622         return panel;
43623     },
43624
43625     /**
43626      * Get the active panel for this region.
43627      * @return {Roo.ContentPanel} The active panel or null
43628      */
43629     getActivePanel : function(){
43630         return this.activePanel;
43631     },
43632
43633     validateVisibility : function(){
43634         if(this.panels.getCount() < 1){
43635             this.updateTitle("&#160;");
43636             this.closeBtn.hide();
43637             this.hide();
43638         }else{
43639             if(!this.isVisible()){
43640                 this.show();
43641             }
43642         }
43643     },
43644
43645     /**
43646      * Adds the passed ContentPanel(s) to this region.
43647      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43648      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
43649      */
43650     add : function(panel)
43651     {
43652         if(arguments.length > 1){
43653             for(var i = 0, len = arguments.length; i < len; i++) {
43654                 this.add(arguments[i]);
43655             }
43656             return null;
43657         }
43658         
43659         // if we have not been rendered yet, then we can not really do much of this..
43660         if (!this.bodyEl) {
43661             this.unrendered_panels.push(panel);
43662             return panel;
43663         }
43664         
43665         
43666         
43667         
43668         if(this.hasPanel(panel)){
43669             this.showPanel(panel);
43670             return panel;
43671         }
43672         panel.setRegion(this);
43673         this.panels.add(panel);
43674        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
43675             // sinle panel - no tab...?? would it not be better to render it with the tabs,
43676             // and hide them... ???
43677             this.bodyEl.dom.appendChild(panel.getEl().dom);
43678             if(panel.background !== true){
43679                 this.setActivePanel(panel);
43680             }
43681             this.fireEvent("paneladded", this, panel);
43682             return panel;
43683         }
43684         */
43685         if(!this.tabs){
43686             this.initTabs();
43687         }else{
43688             this.initPanelAsTab(panel);
43689         }
43690         
43691         
43692         if(panel.background !== true){
43693             this.tabs.activate(panel.getEl().id);
43694         }
43695         this.fireEvent("paneladded", this, panel);
43696         return panel;
43697     },
43698
43699     /**
43700      * Hides the tab for the specified panel.
43701      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43702      */
43703     hidePanel : function(panel){
43704         if(this.tabs && (panel = this.getPanel(panel))){
43705             this.tabs.hideTab(panel.getEl().id);
43706         }
43707     },
43708
43709     /**
43710      * Unhides the tab for a previously hidden panel.
43711      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43712      */
43713     unhidePanel : function(panel){
43714         if(this.tabs && (panel = this.getPanel(panel))){
43715             this.tabs.unhideTab(panel.getEl().id);
43716         }
43717     },
43718
43719     clearPanels : function(){
43720         while(this.panels.getCount() > 0){
43721              this.remove(this.panels.first());
43722         }
43723     },
43724
43725     /**
43726      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43727      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43728      * @param {Boolean} preservePanel Overrides the config preservePanel option
43729      * @return {Roo.ContentPanel} The panel that was removed
43730      */
43731     remove : function(panel, preservePanel)
43732     {
43733         panel = this.getPanel(panel);
43734         if(!panel){
43735             return null;
43736         }
43737         var e = {};
43738         this.fireEvent("beforeremove", this, panel, e);
43739         if(e.cancel === true){
43740             return null;
43741         }
43742         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
43743         var panelId = panel.getId();
43744         this.panels.removeKey(panelId);
43745         if(preservePanel){
43746             document.body.appendChild(panel.getEl().dom);
43747         }
43748         if(this.tabs){
43749             this.tabs.removeTab(panel.getEl().id);
43750         }else if (!preservePanel){
43751             this.bodyEl.dom.removeChild(panel.getEl().dom);
43752         }
43753         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
43754             var p = this.panels.first();
43755             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
43756             tempEl.appendChild(p.getEl().dom);
43757             this.bodyEl.update("");
43758             this.bodyEl.dom.appendChild(p.getEl().dom);
43759             tempEl = null;
43760             this.updateTitle(p.getTitle());
43761             this.tabs = null;
43762             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43763             this.setActivePanel(p);
43764         }
43765         panel.setRegion(null);
43766         if(this.activePanel == panel){
43767             this.activePanel = null;
43768         }
43769         if(this.config.autoDestroy !== false && preservePanel !== true){
43770             try{panel.destroy();}catch(e){}
43771         }
43772         this.fireEvent("panelremoved", this, panel);
43773         return panel;
43774     },
43775
43776     /**
43777      * Returns the TabPanel component used by this region
43778      * @return {Roo.TabPanel}
43779      */
43780     getTabs : function(){
43781         return this.tabs;
43782     },
43783
43784     createTool : function(parentEl, className){
43785         var btn = Roo.DomHelper.append(parentEl, {
43786             tag: "div",
43787             cls: "x-layout-tools-button",
43788             children: [ {
43789                 tag: "div",
43790                 cls: "roo-layout-tools-button-inner " + className,
43791                 html: "&#160;"
43792             }]
43793         }, true);
43794         btn.addClassOnOver("roo-layout-tools-button-over");
43795         return btn;
43796     }
43797 });/*
43798  * Based on:
43799  * Ext JS Library 1.1.1
43800  * Copyright(c) 2006-2007, Ext JS, LLC.
43801  *
43802  * Originally Released Under LGPL - original licence link has changed is not relivant.
43803  *
43804  * Fork - LGPL
43805  * <script type="text/javascript">
43806  */
43807  
43808
43809
43810 /**
43811  * @class Roo.SplitLayoutRegion
43812  * @extends Roo.LayoutRegion
43813  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
43814  */
43815 Roo.bootstrap.layout.Split = function(config){
43816     this.cursor = config.cursor;
43817     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
43818 };
43819
43820 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
43821 {
43822     splitTip : "Drag to resize.",
43823     collapsibleSplitTip : "Drag to resize. Double click to hide.",
43824     useSplitTips : false,
43825
43826     applyConfig : function(config){
43827         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
43828     },
43829     
43830     onRender : function(ctr,pos) {
43831         
43832         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
43833         if(!this.config.split){
43834             return;
43835         }
43836         if(!this.split){
43837             
43838             var splitEl = Roo.DomHelper.append(ctr.dom,  {
43839                             tag: "div",
43840                             id: this.el.id + "-split",
43841                             cls: "roo-layout-split roo-layout-split-"+this.position,
43842                             html: "&#160;"
43843             });
43844             /** The SplitBar for this region 
43845             * @type Roo.SplitBar */
43846             // does not exist yet...
43847             Roo.log([this.position, this.orientation]);
43848             
43849             this.split = new Roo.bootstrap.SplitBar({
43850                 dragElement : splitEl,
43851                 resizingElement: this.el,
43852                 orientation : this.orientation
43853             });
43854             
43855             this.split.on("moved", this.onSplitMove, this);
43856             this.split.useShim = this.config.useShim === true;
43857             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
43858             if(this.useSplitTips){
43859                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
43860             }
43861             //if(config.collapsible){
43862             //    this.split.el.on("dblclick", this.collapse,  this);
43863             //}
43864         }
43865         if(typeof this.config.minSize != "undefined"){
43866             this.split.minSize = this.config.minSize;
43867         }
43868         if(typeof this.config.maxSize != "undefined"){
43869             this.split.maxSize = this.config.maxSize;
43870         }
43871         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
43872             this.hideSplitter();
43873         }
43874         
43875     },
43876
43877     getHMaxSize : function(){
43878          var cmax = this.config.maxSize || 10000;
43879          var center = this.mgr.getRegion("center");
43880          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
43881     },
43882
43883     getVMaxSize : function(){
43884          var cmax = this.config.maxSize || 10000;
43885          var center = this.mgr.getRegion("center");
43886          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
43887     },
43888
43889     onSplitMove : function(split, newSize){
43890         this.fireEvent("resized", this, newSize);
43891     },
43892     
43893     /** 
43894      * Returns the {@link Roo.SplitBar} for this region.
43895      * @return {Roo.SplitBar}
43896      */
43897     getSplitBar : function(){
43898         return this.split;
43899     },
43900     
43901     hide : function(){
43902         this.hideSplitter();
43903         Roo.bootstrap.layout.Split.superclass.hide.call(this);
43904     },
43905
43906     hideSplitter : function(){
43907         if(this.split){
43908             this.split.el.setLocation(-2000,-2000);
43909             this.split.el.hide();
43910         }
43911     },
43912
43913     show : function(){
43914         if(this.split){
43915             this.split.el.show();
43916         }
43917         Roo.bootstrap.layout.Split.superclass.show.call(this);
43918     },
43919     
43920     beforeSlide: function(){
43921         if(Roo.isGecko){// firefox overflow auto bug workaround
43922             this.bodyEl.clip();
43923             if(this.tabs) {
43924                 this.tabs.bodyEl.clip();
43925             }
43926             if(this.activePanel){
43927                 this.activePanel.getEl().clip();
43928                 
43929                 if(this.activePanel.beforeSlide){
43930                     this.activePanel.beforeSlide();
43931                 }
43932             }
43933         }
43934     },
43935     
43936     afterSlide : function(){
43937         if(Roo.isGecko){// firefox overflow auto bug workaround
43938             this.bodyEl.unclip();
43939             if(this.tabs) {
43940                 this.tabs.bodyEl.unclip();
43941             }
43942             if(this.activePanel){
43943                 this.activePanel.getEl().unclip();
43944                 if(this.activePanel.afterSlide){
43945                     this.activePanel.afterSlide();
43946                 }
43947             }
43948         }
43949     },
43950
43951     initAutoHide : function(){
43952         if(this.autoHide !== false){
43953             if(!this.autoHideHd){
43954                 var st = new Roo.util.DelayedTask(this.slideIn, this);
43955                 this.autoHideHd = {
43956                     "mouseout": function(e){
43957                         if(!e.within(this.el, true)){
43958                             st.delay(500);
43959                         }
43960                     },
43961                     "mouseover" : function(e){
43962                         st.cancel();
43963                     },
43964                     scope : this
43965                 };
43966             }
43967             this.el.on(this.autoHideHd);
43968         }
43969     },
43970
43971     clearAutoHide : function(){
43972         if(this.autoHide !== false){
43973             this.el.un("mouseout", this.autoHideHd.mouseout);
43974             this.el.un("mouseover", this.autoHideHd.mouseover);
43975         }
43976     },
43977
43978     clearMonitor : function(){
43979         Roo.get(document).un("click", this.slideInIf, this);
43980     },
43981
43982     // these names are backwards but not changed for compat
43983     slideOut : function(){
43984         if(this.isSlid || this.el.hasActiveFx()){
43985             return;
43986         }
43987         this.isSlid = true;
43988         if(this.collapseBtn){
43989             this.collapseBtn.hide();
43990         }
43991         this.closeBtnState = this.closeBtn.getStyle('display');
43992         this.closeBtn.hide();
43993         if(this.stickBtn){
43994             this.stickBtn.show();
43995         }
43996         this.el.show();
43997         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
43998         this.beforeSlide();
43999         this.el.setStyle("z-index", 10001);
44000         this.el.slideIn(this.getSlideAnchor(), {
44001             callback: function(){
44002                 this.afterSlide();
44003                 this.initAutoHide();
44004                 Roo.get(document).on("click", this.slideInIf, this);
44005                 this.fireEvent("slideshow", this);
44006             },
44007             scope: this,
44008             block: true
44009         });
44010     },
44011
44012     afterSlideIn : function(){
44013         this.clearAutoHide();
44014         this.isSlid = false;
44015         this.clearMonitor();
44016         this.el.setStyle("z-index", "");
44017         if(this.collapseBtn){
44018             this.collapseBtn.show();
44019         }
44020         this.closeBtn.setStyle('display', this.closeBtnState);
44021         if(this.stickBtn){
44022             this.stickBtn.hide();
44023         }
44024         this.fireEvent("slidehide", this);
44025     },
44026
44027     slideIn : function(cb){
44028         if(!this.isSlid || this.el.hasActiveFx()){
44029             Roo.callback(cb);
44030             return;
44031         }
44032         this.isSlid = false;
44033         this.beforeSlide();
44034         this.el.slideOut(this.getSlideAnchor(), {
44035             callback: function(){
44036                 this.el.setLeftTop(-10000, -10000);
44037                 this.afterSlide();
44038                 this.afterSlideIn();
44039                 Roo.callback(cb);
44040             },
44041             scope: this,
44042             block: true
44043         });
44044     },
44045     
44046     slideInIf : function(e){
44047         if(!e.within(this.el)){
44048             this.slideIn();
44049         }
44050     },
44051
44052     animateCollapse : function(){
44053         this.beforeSlide();
44054         this.el.setStyle("z-index", 20000);
44055         var anchor = this.getSlideAnchor();
44056         this.el.slideOut(anchor, {
44057             callback : function(){
44058                 this.el.setStyle("z-index", "");
44059                 this.collapsedEl.slideIn(anchor, {duration:.3});
44060                 this.afterSlide();
44061                 this.el.setLocation(-10000,-10000);
44062                 this.el.hide();
44063                 this.fireEvent("collapsed", this);
44064             },
44065             scope: this,
44066             block: true
44067         });
44068     },
44069
44070     animateExpand : function(){
44071         this.beforeSlide();
44072         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44073         this.el.setStyle("z-index", 20000);
44074         this.collapsedEl.hide({
44075             duration:.1
44076         });
44077         this.el.slideIn(this.getSlideAnchor(), {
44078             callback : function(){
44079                 this.el.setStyle("z-index", "");
44080                 this.afterSlide();
44081                 if(this.split){
44082                     this.split.el.show();
44083                 }
44084                 this.fireEvent("invalidated", this);
44085                 this.fireEvent("expanded", this);
44086             },
44087             scope: this,
44088             block: true
44089         });
44090     },
44091
44092     anchors : {
44093         "west" : "left",
44094         "east" : "right",
44095         "north" : "top",
44096         "south" : "bottom"
44097     },
44098
44099     sanchors : {
44100         "west" : "l",
44101         "east" : "r",
44102         "north" : "t",
44103         "south" : "b"
44104     },
44105
44106     canchors : {
44107         "west" : "tl-tr",
44108         "east" : "tr-tl",
44109         "north" : "tl-bl",
44110         "south" : "bl-tl"
44111     },
44112
44113     getAnchor : function(){
44114         return this.anchors[this.position];
44115     },
44116
44117     getCollapseAnchor : function(){
44118         return this.canchors[this.position];
44119     },
44120
44121     getSlideAnchor : function(){
44122         return this.sanchors[this.position];
44123     },
44124
44125     getAlignAdj : function(){
44126         var cm = this.cmargins;
44127         switch(this.position){
44128             case "west":
44129                 return [0, 0];
44130             break;
44131             case "east":
44132                 return [0, 0];
44133             break;
44134             case "north":
44135                 return [0, 0];
44136             break;
44137             case "south":
44138                 return [0, 0];
44139             break;
44140         }
44141     },
44142
44143     getExpandAdj : function(){
44144         var c = this.collapsedEl, cm = this.cmargins;
44145         switch(this.position){
44146             case "west":
44147                 return [-(cm.right+c.getWidth()+cm.left), 0];
44148             break;
44149             case "east":
44150                 return [cm.right+c.getWidth()+cm.left, 0];
44151             break;
44152             case "north":
44153                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44154             break;
44155             case "south":
44156                 return [0, cm.top+cm.bottom+c.getHeight()];
44157             break;
44158         }
44159     }
44160 });/*
44161  * Based on:
44162  * Ext JS Library 1.1.1
44163  * Copyright(c) 2006-2007, Ext JS, LLC.
44164  *
44165  * Originally Released Under LGPL - original licence link has changed is not relivant.
44166  *
44167  * Fork - LGPL
44168  * <script type="text/javascript">
44169  */
44170 /*
44171  * These classes are private internal classes
44172  */
44173 Roo.bootstrap.layout.Center = function(config){
44174     config.region = "center";
44175     Roo.bootstrap.layout.Region.call(this, config);
44176     this.visible = true;
44177     this.minWidth = config.minWidth || 20;
44178     this.minHeight = config.minHeight || 20;
44179 };
44180
44181 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
44182     hide : function(){
44183         // center panel can't be hidden
44184     },
44185     
44186     show : function(){
44187         // center panel can't be hidden
44188     },
44189     
44190     getMinWidth: function(){
44191         return this.minWidth;
44192     },
44193     
44194     getMinHeight: function(){
44195         return this.minHeight;
44196     }
44197 });
44198
44199
44200
44201
44202  
44203
44204
44205
44206
44207
44208
44209 Roo.bootstrap.layout.North = function(config)
44210 {
44211     config.region = 'north';
44212     config.cursor = 'n-resize';
44213     
44214     Roo.bootstrap.layout.Split.call(this, config);
44215     
44216     
44217     if(this.split){
44218         this.split.placement = Roo.bootstrap.SplitBar.TOP;
44219         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44220         this.split.el.addClass("roo-layout-split-v");
44221     }
44222     //var size = config.initialSize || config.height;
44223     //if(this.el && typeof size != "undefined"){
44224     //    this.el.setHeight(size);
44225     //}
44226 };
44227 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
44228 {
44229     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44230      
44231      
44232     onRender : function(ctr, pos)
44233     {
44234         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44235         var size = this.config.initialSize || this.config.height;
44236         if(this.el && typeof size != "undefined"){
44237             this.el.setHeight(size);
44238         }
44239     
44240     },
44241     
44242     getBox : function(){
44243         if(this.collapsed){
44244             return this.collapsedEl.getBox();
44245         }
44246         var box = this.el.getBox();
44247         if(this.split){
44248             box.height += this.split.el.getHeight();
44249         }
44250         return box;
44251     },
44252     
44253     updateBox : function(box){
44254         if(this.split && !this.collapsed){
44255             box.height -= this.split.el.getHeight();
44256             this.split.el.setLeft(box.x);
44257             this.split.el.setTop(box.y+box.height);
44258             this.split.el.setWidth(box.width);
44259         }
44260         if(this.collapsed){
44261             this.updateBody(box.width, null);
44262         }
44263         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44264     }
44265 });
44266
44267
44268
44269
44270
44271 Roo.bootstrap.layout.South = function(config){
44272     config.region = 'south';
44273     config.cursor = 's-resize';
44274     Roo.bootstrap.layout.Split.call(this, config);
44275     if(this.split){
44276         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
44277         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44278         this.split.el.addClass("roo-layout-split-v");
44279     }
44280     
44281 };
44282
44283 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
44284     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44285     
44286     onRender : function(ctr, pos)
44287     {
44288         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44289         var size = this.config.initialSize || this.config.height;
44290         if(this.el && typeof size != "undefined"){
44291             this.el.setHeight(size);
44292         }
44293     
44294     },
44295     
44296     getBox : function(){
44297         if(this.collapsed){
44298             return this.collapsedEl.getBox();
44299         }
44300         var box = this.el.getBox();
44301         if(this.split){
44302             var sh = this.split.el.getHeight();
44303             box.height += sh;
44304             box.y -= sh;
44305         }
44306         return box;
44307     },
44308     
44309     updateBox : function(box){
44310         if(this.split && !this.collapsed){
44311             var sh = this.split.el.getHeight();
44312             box.height -= sh;
44313             box.y += sh;
44314             this.split.el.setLeft(box.x);
44315             this.split.el.setTop(box.y-sh);
44316             this.split.el.setWidth(box.width);
44317         }
44318         if(this.collapsed){
44319             this.updateBody(box.width, null);
44320         }
44321         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44322     }
44323 });
44324
44325 Roo.bootstrap.layout.East = function(config){
44326     config.region = "east";
44327     config.cursor = "e-resize";
44328     Roo.bootstrap.layout.Split.call(this, config);
44329     if(this.split){
44330         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
44331         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44332         this.split.el.addClass("roo-layout-split-h");
44333     }
44334     
44335 };
44336 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
44337     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44338     
44339     onRender : function(ctr, pos)
44340     {
44341         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44342         var size = this.config.initialSize || this.config.width;
44343         if(this.el && typeof size != "undefined"){
44344             this.el.setWidth(size);
44345         }
44346     
44347     },
44348     
44349     getBox : function(){
44350         if(this.collapsed){
44351             return this.collapsedEl.getBox();
44352         }
44353         var box = this.el.getBox();
44354         if(this.split){
44355             var sw = this.split.el.getWidth();
44356             box.width += sw;
44357             box.x -= sw;
44358         }
44359         return box;
44360     },
44361
44362     updateBox : function(box){
44363         if(this.split && !this.collapsed){
44364             var sw = this.split.el.getWidth();
44365             box.width -= sw;
44366             this.split.el.setLeft(box.x);
44367             this.split.el.setTop(box.y);
44368             this.split.el.setHeight(box.height);
44369             box.x += sw;
44370         }
44371         if(this.collapsed){
44372             this.updateBody(null, box.height);
44373         }
44374         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44375     }
44376 });
44377
44378 Roo.bootstrap.layout.West = function(config){
44379     config.region = "west";
44380     config.cursor = "w-resize";
44381     
44382     Roo.bootstrap.layout.Split.call(this, config);
44383     if(this.split){
44384         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
44385         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44386         this.split.el.addClass("roo-layout-split-h");
44387     }
44388     
44389 };
44390 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
44391     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44392     
44393     onRender: function(ctr, pos)
44394     {
44395         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
44396         var size = this.config.initialSize || this.config.width;
44397         if(typeof size != "undefined"){
44398             this.el.setWidth(size);
44399         }
44400     },
44401     
44402     getBox : function(){
44403         if(this.collapsed){
44404             return this.collapsedEl.getBox();
44405         }
44406         var box = this.el.getBox();
44407         if (box.width == 0) {
44408             box.width = this.config.width; // kludge?
44409         }
44410         if(this.split){
44411             box.width += this.split.el.getWidth();
44412         }
44413         return box;
44414     },
44415     
44416     updateBox : function(box){
44417         if(this.split && !this.collapsed){
44418             var sw = this.split.el.getWidth();
44419             box.width -= sw;
44420             this.split.el.setLeft(box.x+box.width);
44421             this.split.el.setTop(box.y);
44422             this.split.el.setHeight(box.height);
44423         }
44424         if(this.collapsed){
44425             this.updateBody(null, box.height);
44426         }
44427         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44428     }
44429 });/*
44430  * Based on:
44431  * Ext JS Library 1.1.1
44432  * Copyright(c) 2006-2007, Ext JS, LLC.
44433  *
44434  * Originally Released Under LGPL - original licence link has changed is not relivant.
44435  *
44436  * Fork - LGPL
44437  * <script type="text/javascript">
44438  */
44439 /**
44440  * @class Roo.bootstrap.paenl.Content
44441  * @extends Roo.util.Observable
44442  * @children Roo.bootstrap.Component
44443  * @parent builder Roo.bootstrap.layout.Border
44444  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
44445  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
44446  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
44447  * @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
44448  * @cfg {Boolean}   closable      True if the panel can be closed/removed
44449  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
44450  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44451  * @cfg {Toolbar}   toolbar       A toolbar for this panel
44452  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
44453  * @cfg {String} title          The title for this panel
44454  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44455  * @cfg {String} url            Calls {@link #setUrl} with this value
44456  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
44457  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
44458  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
44459  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
44460  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
44461  * @cfg {Boolean} badges render the badges
44462  * @cfg {String} cls  extra classes to use  
44463  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
44464  
44465  * @constructor
44466  * Create a new ContentPanel.
44467  * @param {String/Object} config A string to set only the title or a config object
44468  
44469  */
44470 Roo.bootstrap.panel.Content = function( config){
44471     
44472     this.tpl = config.tpl || false;
44473     
44474     var el = config.el;
44475     var content = config.content;
44476
44477     if(config.autoCreate){ // xtype is available if this is called from factory
44478         el = Roo.id();
44479     }
44480     this.el = Roo.get(el);
44481     if(!this.el && config && config.autoCreate){
44482         if(typeof config.autoCreate == "object"){
44483             if(!config.autoCreate.id){
44484                 config.autoCreate.id = config.id||el;
44485             }
44486             this.el = Roo.DomHelper.append(document.body,
44487                         config.autoCreate, true);
44488         }else{
44489             var elcfg =  {
44490                 tag: "div",
44491                 cls: (config.cls || '') +
44492                     (config.background ? ' bg-' + config.background : '') +
44493                     " roo-layout-inactive-content",
44494                 id: config.id||el
44495             };
44496             if (config.iframe) {
44497                 elcfg.cn = [
44498                     {
44499                         tag : 'iframe',
44500                         style : 'border: 0px',
44501                         src : 'about:blank'
44502                     }
44503                 ];
44504             }
44505               
44506             if (config.html) {
44507                 elcfg.html = config.html;
44508                 
44509             }
44510                         
44511             this.el = Roo.DomHelper.append(document.body, elcfg , true);
44512             if (config.iframe) {
44513                 this.iframeEl = this.el.select('iframe',true).first();
44514             }
44515             
44516         }
44517     } 
44518     this.closable = false;
44519     this.loaded = false;
44520     this.active = false;
44521    
44522       
44523     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
44524         
44525         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
44526         
44527         this.wrapEl = this.el; //this.el.wrap();
44528         var ti = [];
44529         if (config.toolbar.items) {
44530             ti = config.toolbar.items ;
44531             delete config.toolbar.items ;
44532         }
44533         
44534         var nitems = [];
44535         this.toolbar.render(this.wrapEl, 'before');
44536         for(var i =0;i < ti.length;i++) {
44537           //  Roo.log(['add child', items[i]]);
44538             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
44539         }
44540         this.toolbar.items = nitems;
44541         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
44542         delete config.toolbar;
44543         
44544     }
44545     /*
44546     // xtype created footer. - not sure if will work as we normally have to render first..
44547     if (this.footer && !this.footer.el && this.footer.xtype) {
44548         if (!this.wrapEl) {
44549             this.wrapEl = this.el.wrap();
44550         }
44551     
44552         this.footer.container = this.wrapEl.createChild();
44553          
44554         this.footer = Roo.factory(this.footer, Roo);
44555         
44556     }
44557     */
44558     
44559      if(typeof config == "string"){
44560         this.title = config;
44561     }else{
44562         Roo.apply(this, config);
44563     }
44564     
44565     if(this.resizeEl){
44566         this.resizeEl = Roo.get(this.resizeEl, true);
44567     }else{
44568         this.resizeEl = this.el;
44569     }
44570     // handle view.xtype
44571     
44572  
44573     
44574     
44575     this.addEvents({
44576         /**
44577          * @event activate
44578          * Fires when this panel is activated. 
44579          * @param {Roo.ContentPanel} this
44580          */
44581         "activate" : true,
44582         /**
44583          * @event deactivate
44584          * Fires when this panel is activated. 
44585          * @param {Roo.ContentPanel} this
44586          */
44587         "deactivate" : true,
44588
44589         /**
44590          * @event resize
44591          * Fires when this panel is resized if fitToFrame is true.
44592          * @param {Roo.ContentPanel} this
44593          * @param {Number} width The width after any component adjustments
44594          * @param {Number} height The height after any component adjustments
44595          */
44596         "resize" : true,
44597         
44598          /**
44599          * @event render
44600          * Fires when this tab is created
44601          * @param {Roo.ContentPanel} this
44602          */
44603         "render" : true,
44604         
44605           /**
44606          * @event scroll
44607          * Fires when this content is scrolled
44608          * @param {Roo.ContentPanel} this
44609          * @param {Event} scrollEvent
44610          */
44611         "scroll" : true
44612         
44613         
44614         
44615     });
44616     
44617
44618     
44619     
44620     if(this.autoScroll && !this.iframe){
44621         this.resizeEl.setStyle("overflow", "auto");
44622         this.resizeEl.on('scroll', this.onScroll, this);
44623     } else {
44624         // fix randome scrolling
44625         //this.el.on('scroll', function() {
44626         //    Roo.log('fix random scolling');
44627         //    this.scrollTo('top',0); 
44628         //});
44629     }
44630     content = content || this.content;
44631     if(content){
44632         this.setContent(content);
44633     }
44634     if(config && config.url){
44635         this.setUrl(this.url, this.params, this.loadOnce);
44636     }
44637     
44638     
44639     
44640     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
44641     
44642     if (this.view && typeof(this.view.xtype) != 'undefined') {
44643         this.view.el = this.el.appendChild(document.createElement("div"));
44644         this.view = Roo.factory(this.view); 
44645         this.view.render  &&  this.view.render(false, '');  
44646     }
44647     
44648     
44649     this.fireEvent('render', this);
44650 };
44651
44652 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
44653     
44654     cls : '',
44655     background : '',
44656     
44657     tabTip : '',
44658     
44659     iframe : false,
44660     iframeEl : false,
44661     
44662     /* Resize Element - use this to work out scroll etc. */
44663     resizeEl : false,
44664     
44665     setRegion : function(region){
44666         this.region = region;
44667         this.setActiveClass(region && !this.background);
44668     },
44669     
44670     
44671     setActiveClass: function(state)
44672     {
44673         if(state){
44674            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
44675            this.el.setStyle('position','relative');
44676         }else{
44677            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
44678            this.el.setStyle('position', 'absolute');
44679         } 
44680     },
44681     
44682     /**
44683      * Returns the toolbar for this Panel if one was configured. 
44684      * @return {Roo.Toolbar} 
44685      */
44686     getToolbar : function(){
44687         return this.toolbar;
44688     },
44689     
44690     setActiveState : function(active)
44691     {
44692         this.active = active;
44693         this.setActiveClass(active);
44694         if(!active){
44695             if(this.fireEvent("deactivate", this) === false){
44696                 return false;
44697             }
44698             return true;
44699         }
44700         this.fireEvent("activate", this);
44701         return true;
44702     },
44703     /**
44704      * Updates this panel's element (not for iframe)
44705      * @param {String} content The new content
44706      * @param {Boolean} loadScripts (optional) true to look for and process scripts
44707     */
44708     setContent : function(content, loadScripts){
44709         if (this.iframe) {
44710             return;
44711         }
44712         
44713         this.el.update(content, loadScripts);
44714     },
44715
44716     ignoreResize : function(w, h)
44717     {
44718         //return false; // always resize?
44719         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
44720             return true;
44721         }else{
44722             this.lastSize = {width: w, height: h};
44723             return false;
44724         }
44725     },
44726     /**
44727      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
44728      * @return {Roo.UpdateManager} The UpdateManager
44729      */
44730     getUpdateManager : function(){
44731         if (this.iframe) {
44732             return false;
44733         }
44734         return this.el.getUpdateManager();
44735     },
44736      /**
44737      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
44738      * Does not work with IFRAME contents
44739      * @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:
44740 <pre><code>
44741 panel.load({
44742     url: "your-url.php",
44743     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
44744     callback: yourFunction,
44745     scope: yourObject, //(optional scope)
44746     discardUrl: false,
44747     nocache: false,
44748     text: "Loading...",
44749     timeout: 30,
44750     scripts: false
44751 });
44752 </code></pre>
44753      
44754      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
44755      * 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.
44756      * @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}
44757      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
44758      * @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.
44759      * @return {Roo.ContentPanel} this
44760      */
44761     load : function(){
44762         
44763         if (this.iframe) {
44764             return this;
44765         }
44766         
44767         var um = this.el.getUpdateManager();
44768         um.update.apply(um, arguments);
44769         return this;
44770     },
44771
44772
44773     /**
44774      * 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.
44775      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
44776      * @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)
44777      * @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)
44778      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
44779      */
44780     setUrl : function(url, params, loadOnce){
44781         if (this.iframe) {
44782             this.iframeEl.dom.src = url;
44783             return false;
44784         }
44785         
44786         if(this.refreshDelegate){
44787             this.removeListener("activate", this.refreshDelegate);
44788         }
44789         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44790         this.on("activate", this.refreshDelegate);
44791         return this.el.getUpdateManager();
44792     },
44793     
44794     _handleRefresh : function(url, params, loadOnce){
44795         if(!loadOnce || !this.loaded){
44796             var updater = this.el.getUpdateManager();
44797             updater.update(url, params, this._setLoaded.createDelegate(this));
44798         }
44799     },
44800     
44801     _setLoaded : function(){
44802         this.loaded = true;
44803     }, 
44804     
44805     /**
44806      * Returns this panel's id
44807      * @return {String} 
44808      */
44809     getId : function(){
44810         return this.el.id;
44811     },
44812     
44813     /** 
44814      * Returns this panel's element - used by regiosn to add.
44815      * @return {Roo.Element} 
44816      */
44817     getEl : function(){
44818         return this.wrapEl || this.el;
44819     },
44820     
44821    
44822     
44823     adjustForComponents : function(width, height)
44824     {
44825         //Roo.log('adjustForComponents ');
44826         if(this.resizeEl != this.el){
44827             width -= this.el.getFrameWidth('lr');
44828             height -= this.el.getFrameWidth('tb');
44829         }
44830         if(this.toolbar){
44831             var te = this.toolbar.getEl();
44832             te.setWidth(width);
44833             height -= te.getHeight();
44834         }
44835         if(this.footer){
44836             var te = this.footer.getEl();
44837             te.setWidth(width);
44838             height -= te.getHeight();
44839         }
44840         
44841         
44842         if(this.adjustments){
44843             width += this.adjustments[0];
44844             height += this.adjustments[1];
44845         }
44846         return {"width": width, "height": height};
44847     },
44848     
44849     setSize : function(width, height){
44850         if(this.fitToFrame && !this.ignoreResize(width, height)){
44851             if(this.fitContainer && this.resizeEl != this.el){
44852                 this.el.setSize(width, height);
44853             }
44854             var size = this.adjustForComponents(width, height);
44855             if (this.iframe) {
44856                 this.iframeEl.setSize(width,height);
44857             }
44858             
44859             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
44860             this.fireEvent('resize', this, size.width, size.height);
44861             
44862             
44863         }
44864     },
44865     
44866     /**
44867      * Returns this panel's title
44868      * @return {String} 
44869      */
44870     getTitle : function(){
44871         
44872         if (typeof(this.title) != 'object') {
44873             return this.title;
44874         }
44875         
44876         var t = '';
44877         for (var k in this.title) {
44878             if (!this.title.hasOwnProperty(k)) {
44879                 continue;
44880             }
44881             
44882             if (k.indexOf('-') >= 0) {
44883                 var s = k.split('-');
44884                 for (var i = 0; i<s.length; i++) {
44885                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
44886                 }
44887             } else {
44888                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
44889             }
44890         }
44891         return t;
44892     },
44893     
44894     /**
44895      * Set this panel's title
44896      * @param {String} title
44897      */
44898     setTitle : function(title){
44899         this.title = title;
44900         if(this.region){
44901             this.region.updatePanelTitle(this, title);
44902         }
44903     },
44904     
44905     /**
44906      * Returns true is this panel was configured to be closable
44907      * @return {Boolean} 
44908      */
44909     isClosable : function(){
44910         return this.closable;
44911     },
44912     
44913     beforeSlide : function(){
44914         this.el.clip();
44915         this.resizeEl.clip();
44916     },
44917     
44918     afterSlide : function(){
44919         this.el.unclip();
44920         this.resizeEl.unclip();
44921     },
44922     
44923     /**
44924      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
44925      *   Will fail silently if the {@link #setUrl} method has not been called.
44926      *   This does not activate the panel, just updates its content.
44927      */
44928     refresh : function(){
44929         if(this.refreshDelegate){
44930            this.loaded = false;
44931            this.refreshDelegate();
44932         }
44933     },
44934     
44935     /**
44936      * Destroys this panel
44937      */
44938     destroy : function(){
44939         this.el.removeAllListeners();
44940         var tempEl = document.createElement("span");
44941         tempEl.appendChild(this.el.dom);
44942         tempEl.innerHTML = "";
44943         this.el.remove();
44944         this.el = null;
44945     },
44946     
44947     /**
44948      * form - if the content panel contains a form - this is a reference to it.
44949      * @type {Roo.form.Form}
44950      */
44951     form : false,
44952     /**
44953      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
44954      *    This contains a reference to it.
44955      * @type {Roo.View}
44956      */
44957     view : false,
44958     
44959       /**
44960      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
44961      * <pre><code>
44962
44963 layout.addxtype({
44964        xtype : 'Form',
44965        items: [ .... ]
44966    }
44967 );
44968
44969 </code></pre>
44970      * @param {Object} cfg Xtype definition of item to add.
44971      */
44972     
44973     
44974     getChildContainer: function () {
44975         return this.getEl();
44976     },
44977     
44978     
44979     onScroll : function(e)
44980     {
44981         this.fireEvent('scroll', this, e);
44982     }
44983     
44984     
44985     /*
44986         var  ret = new Roo.factory(cfg);
44987         return ret;
44988         
44989         
44990         // add form..
44991         if (cfg.xtype.match(/^Form$/)) {
44992             
44993             var el;
44994             //if (this.footer) {
44995             //    el = this.footer.container.insertSibling(false, 'before');
44996             //} else {
44997                 el = this.el.createChild();
44998             //}
44999
45000             this.form = new  Roo.form.Form(cfg);
45001             
45002             
45003             if ( this.form.allItems.length) {
45004                 this.form.render(el.dom);
45005             }
45006             return this.form;
45007         }
45008         // should only have one of theses..
45009         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45010             // views.. should not be just added - used named prop 'view''
45011             
45012             cfg.el = this.el.appendChild(document.createElement("div"));
45013             // factory?
45014             
45015             var ret = new Roo.factory(cfg);
45016              
45017              ret.render && ret.render(false, ''); // render blank..
45018             this.view = ret;
45019             return ret;
45020         }
45021         return false;
45022     }
45023     \*/
45024 });
45025  
45026 /**
45027  * @class Roo.bootstrap.panel.Grid
45028  * @extends Roo.bootstrap.panel.Content
45029  * @constructor
45030  * Create a new GridPanel.
45031  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45032  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45033  * @param {Object} config A the config object
45034   
45035  */
45036
45037
45038
45039 Roo.bootstrap.panel.Grid = function(config)
45040 {
45041     
45042       
45043     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45044         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45045
45046     config.el = this.wrapper;
45047     //this.el = this.wrapper;
45048     
45049       if (config.container) {
45050         // ctor'ed from a Border/panel.grid
45051         
45052         
45053         this.wrapper.setStyle("overflow", "hidden");
45054         this.wrapper.addClass('roo-grid-container');
45055
45056     }
45057     
45058     
45059     if(config.toolbar){
45060         var tool_el = this.wrapper.createChild();    
45061         this.toolbar = Roo.factory(config.toolbar);
45062         var ti = [];
45063         if (config.toolbar.items) {
45064             ti = config.toolbar.items ;
45065             delete config.toolbar.items ;
45066         }
45067         
45068         var nitems = [];
45069         this.toolbar.render(tool_el);
45070         for(var i =0;i < ti.length;i++) {
45071           //  Roo.log(['add child', items[i]]);
45072             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45073         }
45074         this.toolbar.items = nitems;
45075         
45076         delete config.toolbar;
45077     }
45078     
45079     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45080     config.grid.scrollBody = true;;
45081     config.grid.monitorWindowResize = false; // turn off autosizing
45082     config.grid.autoHeight = false;
45083     config.grid.autoWidth = false;
45084     
45085     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45086     
45087     if (config.background) {
45088         // render grid on panel activation (if panel background)
45089         this.on('activate', function(gp) {
45090             if (!gp.grid.rendered) {
45091                 gp.grid.render(this.wrapper);
45092                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
45093             }
45094         });
45095             
45096     } else {
45097         this.grid.render(this.wrapper);
45098         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
45099
45100     }
45101     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45102     // ??? needed ??? config.el = this.wrapper;
45103     
45104     
45105     
45106   
45107     // xtype created footer. - not sure if will work as we normally have to render first..
45108     if (this.footer && !this.footer.el && this.footer.xtype) {
45109         
45110         var ctr = this.grid.getView().getFooterPanel(true);
45111         this.footer.dataSource = this.grid.dataSource;
45112         this.footer = Roo.factory(this.footer, Roo);
45113         this.footer.render(ctr);
45114         
45115     }
45116     
45117     
45118     
45119     
45120      
45121 };
45122
45123 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45124 {
45125   
45126     getId : function(){
45127         return this.grid.id;
45128     },
45129     
45130     /**
45131      * Returns the grid for this panel
45132      * @return {Roo.bootstrap.Table} 
45133      */
45134     getGrid : function(){
45135         return this.grid;    
45136     },
45137     
45138     setSize : function(width, height)
45139     {
45140      
45141         //if(!this.ignoreResize(width, height)){
45142             var grid = this.grid;
45143             var size = this.adjustForComponents(width, height);
45144             // tfoot is not a footer?
45145           
45146             
45147             var gridel = grid.getGridEl();
45148             gridel.setSize(size.width, size.height);
45149             
45150             var tbd = grid.getGridEl().select('tbody', true).first();
45151             var thd = grid.getGridEl().select('thead',true).first();
45152             var tbf= grid.getGridEl().select('tfoot', true).first();
45153
45154             if (tbf) {
45155                 size.height -= tbf.getHeight();
45156             }
45157             if (thd) {
45158                 size.height -= thd.getHeight();
45159             }
45160             
45161             tbd.setSize(size.width, size.height );
45162             // this is for the account management tab -seems to work there.
45163             var thd = grid.getGridEl().select('thead',true).first();
45164             //if (tbd) {
45165             //    tbd.setSize(size.width, size.height - thd.getHeight());
45166             //}
45167              
45168             grid.autoSize();
45169         //}
45170    
45171     },
45172      
45173     
45174     
45175     beforeSlide : function(){
45176         this.grid.getView().scroller.clip();
45177     },
45178     
45179     afterSlide : function(){
45180         this.grid.getView().scroller.unclip();
45181     },
45182     
45183     destroy : function(){
45184         this.grid.destroy();
45185         delete this.grid;
45186         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
45187     }
45188 });
45189
45190 /**
45191  * @class Roo.bootstrap.panel.Nest
45192  * @extends Roo.bootstrap.panel.Content
45193  * @constructor
45194  * Create a new Panel, that can contain a layout.Border.
45195  * 
45196  * 
45197  * @param {String/Object} config A string to set only the title or a config object
45198  */
45199 Roo.bootstrap.panel.Nest = function(config)
45200 {
45201     // construct with only one argument..
45202     /* FIXME - implement nicer consturctors
45203     if (layout.layout) {
45204         config = layout;
45205         layout = config.layout;
45206         delete config.layout;
45207     }
45208     if (layout.xtype && !layout.getEl) {
45209         // then layout needs constructing..
45210         layout = Roo.factory(layout, Roo);
45211     }
45212     */
45213     
45214     config.el =  config.layout.getEl();
45215     
45216     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
45217     
45218     config.layout.monitorWindowResize = false; // turn off autosizing
45219     this.layout = config.layout;
45220     this.layout.getEl().addClass("roo-layout-nested-layout");
45221     this.layout.parent = this;
45222     
45223     
45224     
45225     
45226 };
45227
45228 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
45229     /**
45230     * @cfg {Roo.BorderLayout} layout The layout for this panel
45231     */
45232     layout : false,
45233
45234     setSize : function(width, height){
45235         if(!this.ignoreResize(width, height)){
45236             var size = this.adjustForComponents(width, height);
45237             var el = this.layout.getEl();
45238             if (size.height < 1) {
45239                 el.setWidth(size.width);   
45240             } else {
45241                 el.setSize(size.width, size.height);
45242             }
45243             var touch = el.dom.offsetWidth;
45244             this.layout.layout();
45245             // ie requires a double layout on the first pass
45246             if(Roo.isIE && !this.initialized){
45247                 this.initialized = true;
45248                 this.layout.layout();
45249             }
45250         }
45251     },
45252     
45253     // activate all subpanels if not currently active..
45254     
45255     setActiveState : function(active){
45256         this.active = active;
45257         this.setActiveClass(active);
45258         
45259         if(!active){
45260             this.fireEvent("deactivate", this);
45261             return;
45262         }
45263         
45264         this.fireEvent("activate", this);
45265         // not sure if this should happen before or after..
45266         if (!this.layout) {
45267             return; // should not happen..
45268         }
45269         var reg = false;
45270         for (var r in this.layout.regions) {
45271             reg = this.layout.getRegion(r);
45272             if (reg.getActivePanel()) {
45273                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
45274                 reg.setActivePanel(reg.getActivePanel());
45275                 continue;
45276             }
45277             if (!reg.panels.length) {
45278                 continue;
45279             }
45280             reg.showPanel(reg.getPanel(0));
45281         }
45282         
45283         
45284         
45285         
45286     },
45287     
45288     /**
45289      * Returns the nested BorderLayout for this panel
45290      * @return {Roo.BorderLayout} 
45291      */
45292     getLayout : function(){
45293         return this.layout;
45294     },
45295     
45296      /**
45297      * Adds a xtype elements to the layout of the nested panel
45298      * <pre><code>
45299
45300 panel.addxtype({
45301        xtype : 'ContentPanel',
45302        region: 'west',
45303        items: [ .... ]
45304    }
45305 );
45306
45307 panel.addxtype({
45308         xtype : 'NestedLayoutPanel',
45309         region: 'west',
45310         layout: {
45311            center: { },
45312            west: { }   
45313         },
45314         items : [ ... list of content panels or nested layout panels.. ]
45315    }
45316 );
45317 </code></pre>
45318      * @param {Object} cfg Xtype definition of item to add.
45319      */
45320     addxtype : function(cfg) {
45321         return this.layout.addxtype(cfg);
45322     
45323     }
45324 });/*
45325  * Based on:
45326  * Ext JS Library 1.1.1
45327  * Copyright(c) 2006-2007, Ext JS, LLC.
45328  *
45329  * Originally Released Under LGPL - original licence link has changed is not relivant.
45330  *
45331  * Fork - LGPL
45332  * <script type="text/javascript">
45333  */
45334 /**
45335  * @class Roo.TabPanel
45336  * @extends Roo.util.Observable
45337  * A lightweight tab container.
45338  * <br><br>
45339  * Usage:
45340  * <pre><code>
45341 // basic tabs 1, built from existing content
45342 var tabs = new Roo.TabPanel("tabs1");
45343 tabs.addTab("script", "View Script");
45344 tabs.addTab("markup", "View Markup");
45345 tabs.activate("script");
45346
45347 // more advanced tabs, built from javascript
45348 var jtabs = new Roo.TabPanel("jtabs");
45349 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
45350
45351 // set up the UpdateManager
45352 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
45353 var updater = tab2.getUpdateManager();
45354 updater.setDefaultUrl("ajax1.htm");
45355 tab2.on('activate', updater.refresh, updater, true);
45356
45357 // Use setUrl for Ajax loading
45358 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
45359 tab3.setUrl("ajax2.htm", null, true);
45360
45361 // Disabled tab
45362 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
45363 tab4.disable();
45364
45365 jtabs.activate("jtabs-1");
45366  * </code></pre>
45367  * @constructor
45368  * Create a new TabPanel.
45369  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
45370  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
45371  */
45372 Roo.bootstrap.panel.Tabs = function(config){
45373     /**
45374     * The container element for this TabPanel.
45375     * @type Roo.Element
45376     */
45377     this.el = Roo.get(config.el);
45378     delete config.el;
45379     if(config){
45380         if(typeof config == "boolean"){
45381             this.tabPosition = config ? "bottom" : "top";
45382         }else{
45383             Roo.apply(this, config);
45384         }
45385     }
45386     
45387     if(this.tabPosition == "bottom"){
45388         // if tabs are at the bottom = create the body first.
45389         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45390         this.el.addClass("roo-tabs-bottom");
45391     }
45392     // next create the tabs holders
45393     
45394     if (this.tabPosition == "west"){
45395         
45396         var reg = this.region; // fake it..
45397         while (reg) {
45398             if (!reg.mgr.parent) {
45399                 break;
45400             }
45401             reg = reg.mgr.parent.region;
45402         }
45403         Roo.log("got nest?");
45404         Roo.log(reg);
45405         if (reg.mgr.getRegion('west')) {
45406             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
45407             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
45408             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45409             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45410             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45411         
45412             
45413         }
45414         
45415         
45416     } else {
45417      
45418         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
45419         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45420         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45421         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45422     }
45423     
45424     
45425     if(Roo.isIE){
45426         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
45427     }
45428     
45429     // finally - if tabs are at the top, then create the body last..
45430     if(this.tabPosition != "bottom"){
45431         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
45432          * @type Roo.Element
45433          */
45434         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45435         this.el.addClass("roo-tabs-top");
45436     }
45437     this.items = [];
45438
45439     this.bodyEl.setStyle("position", "relative");
45440
45441     this.active = null;
45442     this.activateDelegate = this.activate.createDelegate(this);
45443
45444     this.addEvents({
45445         /**
45446          * @event tabchange
45447          * Fires when the active tab changes
45448          * @param {Roo.TabPanel} this
45449          * @param {Roo.TabPanelItem} activePanel The new active tab
45450          */
45451         "tabchange": true,
45452         /**
45453          * @event beforetabchange
45454          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
45455          * @param {Roo.TabPanel} this
45456          * @param {Object} e Set cancel to true on this object to cancel the tab change
45457          * @param {Roo.TabPanelItem} tab The tab being changed to
45458          */
45459         "beforetabchange" : true
45460     });
45461
45462     Roo.EventManager.onWindowResize(this.onResize, this);
45463     this.cpad = this.el.getPadding("lr");
45464     this.hiddenCount = 0;
45465
45466
45467     // toolbar on the tabbar support...
45468     if (this.toolbar) {
45469         alert("no toolbar support yet");
45470         this.toolbar  = false;
45471         /*
45472         var tcfg = this.toolbar;
45473         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
45474         this.toolbar = new Roo.Toolbar(tcfg);
45475         if (Roo.isSafari) {
45476             var tbl = tcfg.container.child('table', true);
45477             tbl.setAttribute('width', '100%');
45478         }
45479         */
45480         
45481     }
45482    
45483
45484
45485     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
45486 };
45487
45488 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
45489     /*
45490      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
45491      */
45492     tabPosition : "top",
45493     /*
45494      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
45495      */
45496     currentTabWidth : 0,
45497     /*
45498      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
45499      */
45500     minTabWidth : 40,
45501     /*
45502      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
45503      */
45504     maxTabWidth : 250,
45505     /*
45506      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
45507      */
45508     preferredTabWidth : 175,
45509     /*
45510      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
45511      */
45512     resizeTabs : false,
45513     /*
45514      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
45515      */
45516     monitorResize : true,
45517     /*
45518      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
45519      */
45520     toolbar : false,  // set by caller..
45521     
45522     region : false, /// set by caller
45523     
45524     disableTooltips : true, // not used yet...
45525
45526     /**
45527      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
45528      * @param {String} id The id of the div to use <b>or create</b>
45529      * @param {String} text The text for the tab
45530      * @param {String} content (optional) Content to put in the TabPanelItem body
45531      * @param {Boolean} closable (optional) True to create a close icon on the tab
45532      * @return {Roo.TabPanelItem} The created TabPanelItem
45533      */
45534     addTab : function(id, text, content, closable, tpl)
45535     {
45536         var item = new Roo.bootstrap.panel.TabItem({
45537             panel: this,
45538             id : id,
45539             text : text,
45540             closable : closable,
45541             tpl : tpl
45542         });
45543         this.addTabItem(item);
45544         if(content){
45545             item.setContent(content);
45546         }
45547         return item;
45548     },
45549
45550     /**
45551      * Returns the {@link Roo.TabPanelItem} with the specified id/index
45552      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
45553      * @return {Roo.TabPanelItem}
45554      */
45555     getTab : function(id){
45556         return this.items[id];
45557     },
45558
45559     /**
45560      * Hides the {@link Roo.TabPanelItem} with the specified id/index
45561      * @param {String/Number} id The id or index of the TabPanelItem to hide.
45562      */
45563     hideTab : function(id){
45564         var t = this.items[id];
45565         if(!t.isHidden()){
45566            t.setHidden(true);
45567            this.hiddenCount++;
45568            this.autoSizeTabs();
45569         }
45570     },
45571
45572     /**
45573      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
45574      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
45575      */
45576     unhideTab : function(id){
45577         var t = this.items[id];
45578         if(t.isHidden()){
45579            t.setHidden(false);
45580            this.hiddenCount--;
45581            this.autoSizeTabs();
45582         }
45583     },
45584
45585     /**
45586      * Adds an existing {@link Roo.TabPanelItem}.
45587      * @param {Roo.TabPanelItem} item The TabPanelItem to add
45588      */
45589     addTabItem : function(item)
45590     {
45591         this.items[item.id] = item;
45592         this.items.push(item);
45593         this.autoSizeTabs();
45594       //  if(this.resizeTabs){
45595     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
45596   //         this.autoSizeTabs();
45597 //        }else{
45598 //            item.autoSize();
45599        // }
45600     },
45601
45602     /**
45603      * Removes a {@link Roo.TabPanelItem}.
45604      * @param {String/Number} id The id or index of the TabPanelItem to remove.
45605      */
45606     removeTab : function(id){
45607         var items = this.items;
45608         var tab = items[id];
45609         if(!tab) { return; }
45610         var index = items.indexOf(tab);
45611         if(this.active == tab && items.length > 1){
45612             var newTab = this.getNextAvailable(index);
45613             if(newTab) {
45614                 newTab.activate();
45615             }
45616         }
45617         this.stripEl.dom.removeChild(tab.pnode.dom);
45618         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
45619             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
45620         }
45621         items.splice(index, 1);
45622         delete this.items[tab.id];
45623         tab.fireEvent("close", tab);
45624         tab.purgeListeners();
45625         this.autoSizeTabs();
45626     },
45627
45628     getNextAvailable : function(start){
45629         var items = this.items;
45630         var index = start;
45631         // look for a next tab that will slide over to
45632         // replace the one being removed
45633         while(index < items.length){
45634             var item = items[++index];
45635             if(item && !item.isHidden()){
45636                 return item;
45637             }
45638         }
45639         // if one isn't found select the previous tab (on the left)
45640         index = start;
45641         while(index >= 0){
45642             var item = items[--index];
45643             if(item && !item.isHidden()){
45644                 return item;
45645             }
45646         }
45647         return null;
45648     },
45649
45650     /**
45651      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
45652      * @param {String/Number} id The id or index of the TabPanelItem to disable.
45653      */
45654     disableTab : function(id){
45655         var tab = this.items[id];
45656         if(tab && this.active != tab){
45657             tab.disable();
45658         }
45659     },
45660
45661     /**
45662      * Enables a {@link Roo.TabPanelItem} that is disabled.
45663      * @param {String/Number} id The id or index of the TabPanelItem to enable.
45664      */
45665     enableTab : function(id){
45666         var tab = this.items[id];
45667         tab.enable();
45668     },
45669
45670     /**
45671      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
45672      * @param {String/Number} id The id or index of the TabPanelItem to activate.
45673      * @return {Roo.TabPanelItem} The TabPanelItem.
45674      */
45675     activate : function(id)
45676     {
45677         //Roo.log('activite:'  + id);
45678         
45679         var tab = this.items[id];
45680         if(!tab){
45681             return null;
45682         }
45683         if(tab == this.active || tab.disabled){
45684             return tab;
45685         }
45686         var e = {};
45687         this.fireEvent("beforetabchange", this, e, tab);
45688         if(e.cancel !== true && !tab.disabled){
45689             if(this.active){
45690                 this.active.hide();
45691             }
45692             this.active = this.items[id];
45693             this.active.show();
45694             this.fireEvent("tabchange", this, this.active);
45695         }
45696         return tab;
45697     },
45698
45699     /**
45700      * Gets the active {@link Roo.TabPanelItem}.
45701      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
45702      */
45703     getActiveTab : function(){
45704         return this.active;
45705     },
45706
45707     /**
45708      * Updates the tab body element to fit the height of the container element
45709      * for overflow scrolling
45710      * @param {Number} targetHeight (optional) Override the starting height from the elements height
45711      */
45712     syncHeight : function(targetHeight){
45713         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
45714         var bm = this.bodyEl.getMargins();
45715         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
45716         this.bodyEl.setHeight(newHeight);
45717         return newHeight;
45718     },
45719
45720     onResize : function(){
45721         if(this.monitorResize){
45722             this.autoSizeTabs();
45723         }
45724     },
45725
45726     /**
45727      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
45728      */
45729     beginUpdate : function(){
45730         this.updating = true;
45731     },
45732
45733     /**
45734      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
45735      */
45736     endUpdate : function(){
45737         this.updating = false;
45738         this.autoSizeTabs();
45739     },
45740
45741     /**
45742      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
45743      */
45744     autoSizeTabs : function()
45745     {
45746         var count = this.items.length;
45747         var vcount = count - this.hiddenCount;
45748         
45749         if (vcount < 2) {
45750             this.stripEl.hide();
45751         } else {
45752             this.stripEl.show();
45753         }
45754         
45755         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
45756             return;
45757         }
45758         
45759         
45760         var w = Math.max(this.el.getWidth() - this.cpad, 10);
45761         var availWidth = Math.floor(w / vcount);
45762         var b = this.stripBody;
45763         if(b.getWidth() > w){
45764             var tabs = this.items;
45765             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
45766             if(availWidth < this.minTabWidth){
45767                 /*if(!this.sleft){    // incomplete scrolling code
45768                     this.createScrollButtons();
45769                 }
45770                 this.showScroll();
45771                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
45772             }
45773         }else{
45774             if(this.currentTabWidth < this.preferredTabWidth){
45775                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
45776             }
45777         }
45778     },
45779
45780     /**
45781      * Returns the number of tabs in this TabPanel.
45782      * @return {Number}
45783      */
45784      getCount : function(){
45785          return this.items.length;
45786      },
45787
45788     /**
45789      * Resizes all the tabs to the passed width
45790      * @param {Number} The new width
45791      */
45792     setTabWidth : function(width){
45793         this.currentTabWidth = width;
45794         for(var i = 0, len = this.items.length; i < len; i++) {
45795                 if(!this.items[i].isHidden()) {
45796                 this.items[i].setWidth(width);
45797             }
45798         }
45799     },
45800
45801     /**
45802      * Destroys this TabPanel
45803      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
45804      */
45805     destroy : function(removeEl){
45806         Roo.EventManager.removeResizeListener(this.onResize, this);
45807         for(var i = 0, len = this.items.length; i < len; i++){
45808             this.items[i].purgeListeners();
45809         }
45810         if(removeEl === true){
45811             this.el.update("");
45812             this.el.remove();
45813         }
45814     },
45815     
45816     createStrip : function(container)
45817     {
45818         var strip = document.createElement("nav");
45819         strip.className = Roo.bootstrap.version == 4 ?
45820             "navbar-light bg-light" : 
45821             "navbar navbar-default"; //"x-tabs-wrap";
45822         container.appendChild(strip);
45823         return strip;
45824     },
45825     
45826     createStripList : function(strip)
45827     {
45828         // div wrapper for retard IE
45829         // returns the "tr" element.
45830         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
45831         //'<div class="x-tabs-strip-wrap">'+
45832           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
45833           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
45834         return strip.firstChild; //.firstChild.firstChild.firstChild;
45835     },
45836     createBody : function(container)
45837     {
45838         var body = document.createElement("div");
45839         Roo.id(body, "tab-body");
45840         //Roo.fly(body).addClass("x-tabs-body");
45841         Roo.fly(body).addClass("tab-content");
45842         container.appendChild(body);
45843         return body;
45844     },
45845     createItemBody :function(bodyEl, id){
45846         var body = Roo.getDom(id);
45847         if(!body){
45848             body = document.createElement("div");
45849             body.id = id;
45850         }
45851         //Roo.fly(body).addClass("x-tabs-item-body");
45852         Roo.fly(body).addClass("tab-pane");
45853          bodyEl.insertBefore(body, bodyEl.firstChild);
45854         return body;
45855     },
45856     /** @private */
45857     createStripElements :  function(stripEl, text, closable, tpl)
45858     {
45859         var td = document.createElement("li"); // was td..
45860         td.className = 'nav-item';
45861         
45862         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
45863         
45864         
45865         stripEl.appendChild(td);
45866         /*if(closable){
45867             td.className = "x-tabs-closable";
45868             if(!this.closeTpl){
45869                 this.closeTpl = new Roo.Template(
45870                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45871                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
45872                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
45873                 );
45874             }
45875             var el = this.closeTpl.overwrite(td, {"text": text});
45876             var close = el.getElementsByTagName("div")[0];
45877             var inner = el.getElementsByTagName("em")[0];
45878             return {"el": el, "close": close, "inner": inner};
45879         } else {
45880         */
45881         // not sure what this is..
45882 //            if(!this.tabTpl){
45883                 //this.tabTpl = new Roo.Template(
45884                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45885                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
45886                 //);
45887 //                this.tabTpl = new Roo.Template(
45888 //                   '<a href="#">' +
45889 //                   '<span unselectable="on"' +
45890 //                            (this.disableTooltips ? '' : ' title="{text}"') +
45891 //                            ' >{text}</span></a>'
45892 //                );
45893 //                
45894 //            }
45895
45896
45897             var template = tpl || this.tabTpl || false;
45898             
45899             if(!template){
45900                 template =  new Roo.Template(
45901                         Roo.bootstrap.version == 4 ? 
45902                             (
45903                                 '<a class="nav-link" href="#" unselectable="on"' +
45904                                      (this.disableTooltips ? '' : ' title="{text}"') +
45905                                      ' >{text}</a>'
45906                             ) : (
45907                                 '<a class="nav-link" href="#">' +
45908                                 '<span unselectable="on"' +
45909                                          (this.disableTooltips ? '' : ' title="{text}"') +
45910                                     ' >{text}</span></a>'
45911                             )
45912                 );
45913             }
45914             
45915             switch (typeof(template)) {
45916                 case 'object' :
45917                     break;
45918                 case 'string' :
45919                     template = new Roo.Template(template);
45920                     break;
45921                 default :
45922                     break;
45923             }
45924             
45925             var el = template.overwrite(td, {"text": text});
45926             
45927             var inner = el.getElementsByTagName("span")[0];
45928             
45929             return {"el": el, "inner": inner};
45930             
45931     }
45932         
45933     
45934 });
45935
45936 /**
45937  * @class Roo.TabPanelItem
45938  * @extends Roo.util.Observable
45939  * Represents an individual item (tab plus body) in a TabPanel.
45940  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
45941  * @param {String} id The id of this TabPanelItem
45942  * @param {String} text The text for the tab of this TabPanelItem
45943  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
45944  */
45945 Roo.bootstrap.panel.TabItem = function(config){
45946     /**
45947      * The {@link Roo.TabPanel} this TabPanelItem belongs to
45948      * @type Roo.TabPanel
45949      */
45950     this.tabPanel = config.panel;
45951     /**
45952      * The id for this TabPanelItem
45953      * @type String
45954      */
45955     this.id = config.id;
45956     /** @private */
45957     this.disabled = false;
45958     /** @private */
45959     this.text = config.text;
45960     /** @private */
45961     this.loaded = false;
45962     this.closable = config.closable;
45963
45964     /**
45965      * The body element for this TabPanelItem.
45966      * @type Roo.Element
45967      */
45968     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
45969     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
45970     this.bodyEl.setStyle("display", "block");
45971     this.bodyEl.setStyle("zoom", "1");
45972     //this.hideAction();
45973
45974     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
45975     /** @private */
45976     this.el = Roo.get(els.el);
45977     this.inner = Roo.get(els.inner, true);
45978      this.textEl = Roo.bootstrap.version == 4 ?
45979         this.el : Roo.get(this.el.dom.firstChild, true);
45980
45981     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
45982     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
45983
45984     
45985 //    this.el.on("mousedown", this.onTabMouseDown, this);
45986     this.el.on("click", this.onTabClick, this);
45987     /** @private */
45988     if(config.closable){
45989         var c = Roo.get(els.close, true);
45990         c.dom.title = this.closeText;
45991         c.addClassOnOver("close-over");
45992         c.on("click", this.closeClick, this);
45993      }
45994
45995     this.addEvents({
45996          /**
45997          * @event activate
45998          * Fires when this tab becomes the active tab.
45999          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46000          * @param {Roo.TabPanelItem} this
46001          */
46002         "activate": true,
46003         /**
46004          * @event beforeclose
46005          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46006          * @param {Roo.TabPanelItem} this
46007          * @param {Object} e Set cancel to true on this object to cancel the close.
46008          */
46009         "beforeclose": true,
46010         /**
46011          * @event close
46012          * Fires when this tab is closed.
46013          * @param {Roo.TabPanelItem} this
46014          */
46015          "close": true,
46016         /**
46017          * @event deactivate
46018          * Fires when this tab is no longer the active tab.
46019          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46020          * @param {Roo.TabPanelItem} this
46021          */
46022          "deactivate" : true
46023     });
46024     this.hidden = false;
46025
46026     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46027 };
46028
46029 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46030            {
46031     purgeListeners : function(){
46032        Roo.util.Observable.prototype.purgeListeners.call(this);
46033        this.el.removeAllListeners();
46034     },
46035     /**
46036      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46037      */
46038     show : function(){
46039         this.status_node.addClass("active");
46040         this.showAction();
46041         if(Roo.isOpera){
46042             this.tabPanel.stripWrap.repaint();
46043         }
46044         this.fireEvent("activate", this.tabPanel, this);
46045     },
46046
46047     /**
46048      * Returns true if this tab is the active tab.
46049      * @return {Boolean}
46050      */
46051     isActive : function(){
46052         return this.tabPanel.getActiveTab() == this;
46053     },
46054
46055     /**
46056      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46057      */
46058     hide : function(){
46059         this.status_node.removeClass("active");
46060         this.hideAction();
46061         this.fireEvent("deactivate", this.tabPanel, this);
46062     },
46063
46064     hideAction : function(){
46065         this.bodyEl.hide();
46066         this.bodyEl.setStyle("position", "absolute");
46067         this.bodyEl.setLeft("-20000px");
46068         this.bodyEl.setTop("-20000px");
46069     },
46070
46071     showAction : function(){
46072         this.bodyEl.setStyle("position", "relative");
46073         this.bodyEl.setTop("");
46074         this.bodyEl.setLeft("");
46075         this.bodyEl.show();
46076     },
46077
46078     /**
46079      * Set the tooltip for the tab.
46080      * @param {String} tooltip The tab's tooltip
46081      */
46082     setTooltip : function(text){
46083         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46084             this.textEl.dom.qtip = text;
46085             this.textEl.dom.removeAttribute('title');
46086         }else{
46087             this.textEl.dom.title = text;
46088         }
46089     },
46090
46091     onTabClick : function(e){
46092         e.preventDefault();
46093         this.tabPanel.activate(this.id);
46094     },
46095
46096     onTabMouseDown : function(e){
46097         e.preventDefault();
46098         this.tabPanel.activate(this.id);
46099     },
46100 /*
46101     getWidth : function(){
46102         return this.inner.getWidth();
46103     },
46104
46105     setWidth : function(width){
46106         var iwidth = width - this.linode.getPadding("lr");
46107         this.inner.setWidth(iwidth);
46108         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46109         this.linode.setWidth(width);
46110     },
46111 */
46112     /**
46113      * Show or hide the tab
46114      * @param {Boolean} hidden True to hide or false to show.
46115      */
46116     setHidden : function(hidden){
46117         this.hidden = hidden;
46118         this.linode.setStyle("display", hidden ? "none" : "");
46119     },
46120
46121     /**
46122      * Returns true if this tab is "hidden"
46123      * @return {Boolean}
46124      */
46125     isHidden : function(){
46126         return this.hidden;
46127     },
46128
46129     /**
46130      * Returns the text for this tab
46131      * @return {String}
46132      */
46133     getText : function(){
46134         return this.text;
46135     },
46136     /*
46137     autoSize : function(){
46138         //this.el.beginMeasure();
46139         this.textEl.setWidth(1);
46140         /*
46141          *  #2804 [new] Tabs in Roojs
46142          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46143          */
46144         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46145         //this.el.endMeasure();
46146     //},
46147
46148     /**
46149      * Sets the text for the tab (Note: this also sets the tooltip text)
46150      * @param {String} text The tab's text and tooltip
46151      */
46152     setText : function(text){
46153         this.text = text;
46154         this.textEl.update(text);
46155         this.setTooltip(text);
46156         //if(!this.tabPanel.resizeTabs){
46157         //    this.autoSize();
46158         //}
46159     },
46160     /**
46161      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46162      */
46163     activate : function(){
46164         this.tabPanel.activate(this.id);
46165     },
46166
46167     /**
46168      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46169      */
46170     disable : function(){
46171         if(this.tabPanel.active != this){
46172             this.disabled = true;
46173             this.status_node.addClass("disabled");
46174         }
46175     },
46176
46177     /**
46178      * Enables this TabPanelItem if it was previously disabled.
46179      */
46180     enable : function(){
46181         this.disabled = false;
46182         this.status_node.removeClass("disabled");
46183     },
46184
46185     /**
46186      * Sets the content for this TabPanelItem.
46187      * @param {String} content The content
46188      * @param {Boolean} loadScripts true to look for and load scripts
46189      */
46190     setContent : function(content, loadScripts){
46191         this.bodyEl.update(content, loadScripts);
46192     },
46193
46194     /**
46195      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
46196      * @return {Roo.UpdateManager} The UpdateManager
46197      */
46198     getUpdateManager : function(){
46199         return this.bodyEl.getUpdateManager();
46200     },
46201
46202     /**
46203      * Set a URL to be used to load the content for this TabPanelItem.
46204      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
46205      * @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)
46206      * @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)
46207      * @return {Roo.UpdateManager} The UpdateManager
46208      */
46209     setUrl : function(url, params, loadOnce){
46210         if(this.refreshDelegate){
46211             this.un('activate', this.refreshDelegate);
46212         }
46213         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46214         this.on("activate", this.refreshDelegate);
46215         return this.bodyEl.getUpdateManager();
46216     },
46217
46218     /** @private */
46219     _handleRefresh : function(url, params, loadOnce){
46220         if(!loadOnce || !this.loaded){
46221             var updater = this.bodyEl.getUpdateManager();
46222             updater.update(url, params, this._setLoaded.createDelegate(this));
46223         }
46224     },
46225
46226     /**
46227      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
46228      *   Will fail silently if the setUrl method has not been called.
46229      *   This does not activate the panel, just updates its content.
46230      */
46231     refresh : function(){
46232         if(this.refreshDelegate){
46233            this.loaded = false;
46234            this.refreshDelegate();
46235         }
46236     },
46237
46238     /** @private */
46239     _setLoaded : function(){
46240         this.loaded = true;
46241     },
46242
46243     /** @private */
46244     closeClick : function(e){
46245         var o = {};
46246         e.stopEvent();
46247         this.fireEvent("beforeclose", this, o);
46248         if(o.cancel !== true){
46249             this.tabPanel.removeTab(this.id);
46250         }
46251     },
46252     /**
46253      * The text displayed in the tooltip for the close icon.
46254      * @type String
46255      */
46256     closeText : "Close this tab"
46257 });
46258 /**
46259 *    This script refer to:
46260 *    Title: International Telephone Input
46261 *    Author: Jack O'Connor
46262 *    Code version:  v12.1.12
46263 *    Availability: https://github.com/jackocnr/intl-tel-input.git
46264 **/
46265
46266 Roo.bootstrap.form.PhoneInputData = function() {
46267     var d = [
46268       [
46269         "Afghanistan (‫افغانستان‬‎)",
46270         "af",
46271         "93"
46272       ],
46273       [
46274         "Albania (Shqipëri)",
46275         "al",
46276         "355"
46277       ],
46278       [
46279         "Algeria (‫الجزائر‬‎)",
46280         "dz",
46281         "213"
46282       ],
46283       [
46284         "American Samoa",
46285         "as",
46286         "1684"
46287       ],
46288       [
46289         "Andorra",
46290         "ad",
46291         "376"
46292       ],
46293       [
46294         "Angola",
46295         "ao",
46296         "244"
46297       ],
46298       [
46299         "Anguilla",
46300         "ai",
46301         "1264"
46302       ],
46303       [
46304         "Antigua and Barbuda",
46305         "ag",
46306         "1268"
46307       ],
46308       [
46309         "Argentina",
46310         "ar",
46311         "54"
46312       ],
46313       [
46314         "Armenia (Հայաստան)",
46315         "am",
46316         "374"
46317       ],
46318       [
46319         "Aruba",
46320         "aw",
46321         "297"
46322       ],
46323       [
46324         "Australia",
46325         "au",
46326         "61",
46327         0
46328       ],
46329       [
46330         "Austria (Österreich)",
46331         "at",
46332         "43"
46333       ],
46334       [
46335         "Azerbaijan (Azərbaycan)",
46336         "az",
46337         "994"
46338       ],
46339       [
46340         "Bahamas",
46341         "bs",
46342         "1242"
46343       ],
46344       [
46345         "Bahrain (‫البحرين‬‎)",
46346         "bh",
46347         "973"
46348       ],
46349       [
46350         "Bangladesh (বাংলাদেশ)",
46351         "bd",
46352         "880"
46353       ],
46354       [
46355         "Barbados",
46356         "bb",
46357         "1246"
46358       ],
46359       [
46360         "Belarus (Беларусь)",
46361         "by",
46362         "375"
46363       ],
46364       [
46365         "Belgium (België)",
46366         "be",
46367         "32"
46368       ],
46369       [
46370         "Belize",
46371         "bz",
46372         "501"
46373       ],
46374       [
46375         "Benin (Bénin)",
46376         "bj",
46377         "229"
46378       ],
46379       [
46380         "Bermuda",
46381         "bm",
46382         "1441"
46383       ],
46384       [
46385         "Bhutan (འབྲུག)",
46386         "bt",
46387         "975"
46388       ],
46389       [
46390         "Bolivia",
46391         "bo",
46392         "591"
46393       ],
46394       [
46395         "Bosnia and Herzegovina (Босна и Херцеговина)",
46396         "ba",
46397         "387"
46398       ],
46399       [
46400         "Botswana",
46401         "bw",
46402         "267"
46403       ],
46404       [
46405         "Brazil (Brasil)",
46406         "br",
46407         "55"
46408       ],
46409       [
46410         "British Indian Ocean Territory",
46411         "io",
46412         "246"
46413       ],
46414       [
46415         "British Virgin Islands",
46416         "vg",
46417         "1284"
46418       ],
46419       [
46420         "Brunei",
46421         "bn",
46422         "673"
46423       ],
46424       [
46425         "Bulgaria (България)",
46426         "bg",
46427         "359"
46428       ],
46429       [
46430         "Burkina Faso",
46431         "bf",
46432         "226"
46433       ],
46434       [
46435         "Burundi (Uburundi)",
46436         "bi",
46437         "257"
46438       ],
46439       [
46440         "Cambodia (កម្ពុជា)",
46441         "kh",
46442         "855"
46443       ],
46444       [
46445         "Cameroon (Cameroun)",
46446         "cm",
46447         "237"
46448       ],
46449       [
46450         "Canada",
46451         "ca",
46452         "1",
46453         1,
46454         ["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"]
46455       ],
46456       [
46457         "Cape Verde (Kabu Verdi)",
46458         "cv",
46459         "238"
46460       ],
46461       [
46462         "Caribbean Netherlands",
46463         "bq",
46464         "599",
46465         1
46466       ],
46467       [
46468         "Cayman Islands",
46469         "ky",
46470         "1345"
46471       ],
46472       [
46473         "Central African Republic (République centrafricaine)",
46474         "cf",
46475         "236"
46476       ],
46477       [
46478         "Chad (Tchad)",
46479         "td",
46480         "235"
46481       ],
46482       [
46483         "Chile",
46484         "cl",
46485         "56"
46486       ],
46487       [
46488         "China (中国)",
46489         "cn",
46490         "86"
46491       ],
46492       [
46493         "Christmas Island",
46494         "cx",
46495         "61",
46496         2
46497       ],
46498       [
46499         "Cocos (Keeling) Islands",
46500         "cc",
46501         "61",
46502         1
46503       ],
46504       [
46505         "Colombia",
46506         "co",
46507         "57"
46508       ],
46509       [
46510         "Comoros (‫جزر القمر‬‎)",
46511         "km",
46512         "269"
46513       ],
46514       [
46515         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
46516         "cd",
46517         "243"
46518       ],
46519       [
46520         "Congo (Republic) (Congo-Brazzaville)",
46521         "cg",
46522         "242"
46523       ],
46524       [
46525         "Cook Islands",
46526         "ck",
46527         "682"
46528       ],
46529       [
46530         "Costa Rica",
46531         "cr",
46532         "506"
46533       ],
46534       [
46535         "Côte d’Ivoire",
46536         "ci",
46537         "225"
46538       ],
46539       [
46540         "Croatia (Hrvatska)",
46541         "hr",
46542         "385"
46543       ],
46544       [
46545         "Cuba",
46546         "cu",
46547         "53"
46548       ],
46549       [
46550         "Curaçao",
46551         "cw",
46552         "599",
46553         0
46554       ],
46555       [
46556         "Cyprus (Κύπρος)",
46557         "cy",
46558         "357"
46559       ],
46560       [
46561         "Czech Republic (Česká republika)",
46562         "cz",
46563         "420"
46564       ],
46565       [
46566         "Denmark (Danmark)",
46567         "dk",
46568         "45"
46569       ],
46570       [
46571         "Djibouti",
46572         "dj",
46573         "253"
46574       ],
46575       [
46576         "Dominica",
46577         "dm",
46578         "1767"
46579       ],
46580       [
46581         "Dominican Republic (República Dominicana)",
46582         "do",
46583         "1",
46584         2,
46585         ["809", "829", "849"]
46586       ],
46587       [
46588         "Ecuador",
46589         "ec",
46590         "593"
46591       ],
46592       [
46593         "Egypt (‫مصر‬‎)",
46594         "eg",
46595         "20"
46596       ],
46597       [
46598         "El Salvador",
46599         "sv",
46600         "503"
46601       ],
46602       [
46603         "Equatorial Guinea (Guinea Ecuatorial)",
46604         "gq",
46605         "240"
46606       ],
46607       [
46608         "Eritrea",
46609         "er",
46610         "291"
46611       ],
46612       [
46613         "Estonia (Eesti)",
46614         "ee",
46615         "372"
46616       ],
46617       [
46618         "Ethiopia",
46619         "et",
46620         "251"
46621       ],
46622       [
46623         "Falkland Islands (Islas Malvinas)",
46624         "fk",
46625         "500"
46626       ],
46627       [
46628         "Faroe Islands (Føroyar)",
46629         "fo",
46630         "298"
46631       ],
46632       [
46633         "Fiji",
46634         "fj",
46635         "679"
46636       ],
46637       [
46638         "Finland (Suomi)",
46639         "fi",
46640         "358",
46641         0
46642       ],
46643       [
46644         "France",
46645         "fr",
46646         "33"
46647       ],
46648       [
46649         "French Guiana (Guyane française)",
46650         "gf",
46651         "594"
46652       ],
46653       [
46654         "French Polynesia (Polynésie française)",
46655         "pf",
46656         "689"
46657       ],
46658       [
46659         "Gabon",
46660         "ga",
46661         "241"
46662       ],
46663       [
46664         "Gambia",
46665         "gm",
46666         "220"
46667       ],
46668       [
46669         "Georgia (საქართველო)",
46670         "ge",
46671         "995"
46672       ],
46673       [
46674         "Germany (Deutschland)",
46675         "de",
46676         "49"
46677       ],
46678       [
46679         "Ghana (Gaana)",
46680         "gh",
46681         "233"
46682       ],
46683       [
46684         "Gibraltar",
46685         "gi",
46686         "350"
46687       ],
46688       [
46689         "Greece (Ελλάδα)",
46690         "gr",
46691         "30"
46692       ],
46693       [
46694         "Greenland (Kalaallit Nunaat)",
46695         "gl",
46696         "299"
46697       ],
46698       [
46699         "Grenada",
46700         "gd",
46701         "1473"
46702       ],
46703       [
46704         "Guadeloupe",
46705         "gp",
46706         "590",
46707         0
46708       ],
46709       [
46710         "Guam",
46711         "gu",
46712         "1671"
46713       ],
46714       [
46715         "Guatemala",
46716         "gt",
46717         "502"
46718       ],
46719       [
46720         "Guernsey",
46721         "gg",
46722         "44",
46723         1
46724       ],
46725       [
46726         "Guinea (Guinée)",
46727         "gn",
46728         "224"
46729       ],
46730       [
46731         "Guinea-Bissau (Guiné Bissau)",
46732         "gw",
46733         "245"
46734       ],
46735       [
46736         "Guyana",
46737         "gy",
46738         "592"
46739       ],
46740       [
46741         "Haiti",
46742         "ht",
46743         "509"
46744       ],
46745       [
46746         "Honduras",
46747         "hn",
46748         "504"
46749       ],
46750       [
46751         "Hong Kong (香港)",
46752         "hk",
46753         "852"
46754       ],
46755       [
46756         "Hungary (Magyarország)",
46757         "hu",
46758         "36"
46759       ],
46760       [
46761         "Iceland (Ísland)",
46762         "is",
46763         "354"
46764       ],
46765       [
46766         "India (भारत)",
46767         "in",
46768         "91"
46769       ],
46770       [
46771         "Indonesia",
46772         "id",
46773         "62"
46774       ],
46775       [
46776         "Iran (‫ایران‬‎)",
46777         "ir",
46778         "98"
46779       ],
46780       [
46781         "Iraq (‫العراق‬‎)",
46782         "iq",
46783         "964"
46784       ],
46785       [
46786         "Ireland",
46787         "ie",
46788         "353"
46789       ],
46790       [
46791         "Isle of Man",
46792         "im",
46793         "44",
46794         2
46795       ],
46796       [
46797         "Israel (‫ישראל‬‎)",
46798         "il",
46799         "972"
46800       ],
46801       [
46802         "Italy (Italia)",
46803         "it",
46804         "39",
46805         0
46806       ],
46807       [
46808         "Jamaica",
46809         "jm",
46810         "1876"
46811       ],
46812       [
46813         "Japan (日本)",
46814         "jp",
46815         "81"
46816       ],
46817       [
46818         "Jersey",
46819         "je",
46820         "44",
46821         3
46822       ],
46823       [
46824         "Jordan (‫الأردن‬‎)",
46825         "jo",
46826         "962"
46827       ],
46828       [
46829         "Kazakhstan (Казахстан)",
46830         "kz",
46831         "7",
46832         1
46833       ],
46834       [
46835         "Kenya",
46836         "ke",
46837         "254"
46838       ],
46839       [
46840         "Kiribati",
46841         "ki",
46842         "686"
46843       ],
46844       [
46845         "Kosovo",
46846         "xk",
46847         "383"
46848       ],
46849       [
46850         "Kuwait (‫الكويت‬‎)",
46851         "kw",
46852         "965"
46853       ],
46854       [
46855         "Kyrgyzstan (Кыргызстан)",
46856         "kg",
46857         "996"
46858       ],
46859       [
46860         "Laos (ລາວ)",
46861         "la",
46862         "856"
46863       ],
46864       [
46865         "Latvia (Latvija)",
46866         "lv",
46867         "371"
46868       ],
46869       [
46870         "Lebanon (‫لبنان‬‎)",
46871         "lb",
46872         "961"
46873       ],
46874       [
46875         "Lesotho",
46876         "ls",
46877         "266"
46878       ],
46879       [
46880         "Liberia",
46881         "lr",
46882         "231"
46883       ],
46884       [
46885         "Libya (‫ليبيا‬‎)",
46886         "ly",
46887         "218"
46888       ],
46889       [
46890         "Liechtenstein",
46891         "li",
46892         "423"
46893       ],
46894       [
46895         "Lithuania (Lietuva)",
46896         "lt",
46897         "370"
46898       ],
46899       [
46900         "Luxembourg",
46901         "lu",
46902         "352"
46903       ],
46904       [
46905         "Macau (澳門)",
46906         "mo",
46907         "853"
46908       ],
46909       [
46910         "Macedonia (FYROM) (Македонија)",
46911         "mk",
46912         "389"
46913       ],
46914       [
46915         "Madagascar (Madagasikara)",
46916         "mg",
46917         "261"
46918       ],
46919       [
46920         "Malawi",
46921         "mw",
46922         "265"
46923       ],
46924       [
46925         "Malaysia",
46926         "my",
46927         "60"
46928       ],
46929       [
46930         "Maldives",
46931         "mv",
46932         "960"
46933       ],
46934       [
46935         "Mali",
46936         "ml",
46937         "223"
46938       ],
46939       [
46940         "Malta",
46941         "mt",
46942         "356"
46943       ],
46944       [
46945         "Marshall Islands",
46946         "mh",
46947         "692"
46948       ],
46949       [
46950         "Martinique",
46951         "mq",
46952         "596"
46953       ],
46954       [
46955         "Mauritania (‫موريتانيا‬‎)",
46956         "mr",
46957         "222"
46958       ],
46959       [
46960         "Mauritius (Moris)",
46961         "mu",
46962         "230"
46963       ],
46964       [
46965         "Mayotte",
46966         "yt",
46967         "262",
46968         1
46969       ],
46970       [
46971         "Mexico (México)",
46972         "mx",
46973         "52"
46974       ],
46975       [
46976         "Micronesia",
46977         "fm",
46978         "691"
46979       ],
46980       [
46981         "Moldova (Republica Moldova)",
46982         "md",
46983         "373"
46984       ],
46985       [
46986         "Monaco",
46987         "mc",
46988         "377"
46989       ],
46990       [
46991         "Mongolia (Монгол)",
46992         "mn",
46993         "976"
46994       ],
46995       [
46996         "Montenegro (Crna Gora)",
46997         "me",
46998         "382"
46999       ],
47000       [
47001         "Montserrat",
47002         "ms",
47003         "1664"
47004       ],
47005       [
47006         "Morocco (‫المغرب‬‎)",
47007         "ma",
47008         "212",
47009         0
47010       ],
47011       [
47012         "Mozambique (Moçambique)",
47013         "mz",
47014         "258"
47015       ],
47016       [
47017         "Myanmar (Burma) (မြန်မာ)",
47018         "mm",
47019         "95"
47020       ],
47021       [
47022         "Namibia (Namibië)",
47023         "na",
47024         "264"
47025       ],
47026       [
47027         "Nauru",
47028         "nr",
47029         "674"
47030       ],
47031       [
47032         "Nepal (नेपाल)",
47033         "np",
47034         "977"
47035       ],
47036       [
47037         "Netherlands (Nederland)",
47038         "nl",
47039         "31"
47040       ],
47041       [
47042         "New Caledonia (Nouvelle-Calédonie)",
47043         "nc",
47044         "687"
47045       ],
47046       [
47047         "New Zealand",
47048         "nz",
47049         "64"
47050       ],
47051       [
47052         "Nicaragua",
47053         "ni",
47054         "505"
47055       ],
47056       [
47057         "Niger (Nijar)",
47058         "ne",
47059         "227"
47060       ],
47061       [
47062         "Nigeria",
47063         "ng",
47064         "234"
47065       ],
47066       [
47067         "Niue",
47068         "nu",
47069         "683"
47070       ],
47071       [
47072         "Norfolk Island",
47073         "nf",
47074         "672"
47075       ],
47076       [
47077         "North Korea (조선 민주주의 인민 공화국)",
47078         "kp",
47079         "850"
47080       ],
47081       [
47082         "Northern Mariana Islands",
47083         "mp",
47084         "1670"
47085       ],
47086       [
47087         "Norway (Norge)",
47088         "no",
47089         "47",
47090         0
47091       ],
47092       [
47093         "Oman (‫عُمان‬‎)",
47094         "om",
47095         "968"
47096       ],
47097       [
47098         "Pakistan (‫پاکستان‬‎)",
47099         "pk",
47100         "92"
47101       ],
47102       [
47103         "Palau",
47104         "pw",
47105         "680"
47106       ],
47107       [
47108         "Palestine (‫فلسطين‬‎)",
47109         "ps",
47110         "970"
47111       ],
47112       [
47113         "Panama (Panamá)",
47114         "pa",
47115         "507"
47116       ],
47117       [
47118         "Papua New Guinea",
47119         "pg",
47120         "675"
47121       ],
47122       [
47123         "Paraguay",
47124         "py",
47125         "595"
47126       ],
47127       [
47128         "Peru (Perú)",
47129         "pe",
47130         "51"
47131       ],
47132       [
47133         "Philippines",
47134         "ph",
47135         "63"
47136       ],
47137       [
47138         "Poland (Polska)",
47139         "pl",
47140         "48"
47141       ],
47142       [
47143         "Portugal",
47144         "pt",
47145         "351"
47146       ],
47147       [
47148         "Puerto Rico",
47149         "pr",
47150         "1",
47151         3,
47152         ["787", "939"]
47153       ],
47154       [
47155         "Qatar (‫قطر‬‎)",
47156         "qa",
47157         "974"
47158       ],
47159       [
47160         "Réunion (La Réunion)",
47161         "re",
47162         "262",
47163         0
47164       ],
47165       [
47166         "Romania (România)",
47167         "ro",
47168         "40"
47169       ],
47170       [
47171         "Russia (Россия)",
47172         "ru",
47173         "7",
47174         0
47175       ],
47176       [
47177         "Rwanda",
47178         "rw",
47179         "250"
47180       ],
47181       [
47182         "Saint Barthélemy",
47183         "bl",
47184         "590",
47185         1
47186       ],
47187       [
47188         "Saint Helena",
47189         "sh",
47190         "290"
47191       ],
47192       [
47193         "Saint Kitts and Nevis",
47194         "kn",
47195         "1869"
47196       ],
47197       [
47198         "Saint Lucia",
47199         "lc",
47200         "1758"
47201       ],
47202       [
47203         "Saint Martin (Saint-Martin (partie française))",
47204         "mf",
47205         "590",
47206         2
47207       ],
47208       [
47209         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
47210         "pm",
47211         "508"
47212       ],
47213       [
47214         "Saint Vincent and the Grenadines",
47215         "vc",
47216         "1784"
47217       ],
47218       [
47219         "Samoa",
47220         "ws",
47221         "685"
47222       ],
47223       [
47224         "San Marino",
47225         "sm",
47226         "378"
47227       ],
47228       [
47229         "São Tomé and Príncipe (São Tomé e Príncipe)",
47230         "st",
47231         "239"
47232       ],
47233       [
47234         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
47235         "sa",
47236         "966"
47237       ],
47238       [
47239         "Senegal (Sénégal)",
47240         "sn",
47241         "221"
47242       ],
47243       [
47244         "Serbia (Србија)",
47245         "rs",
47246         "381"
47247       ],
47248       [
47249         "Seychelles",
47250         "sc",
47251         "248"
47252       ],
47253       [
47254         "Sierra Leone",
47255         "sl",
47256         "232"
47257       ],
47258       [
47259         "Singapore",
47260         "sg",
47261         "65"
47262       ],
47263       [
47264         "Sint Maarten",
47265         "sx",
47266         "1721"
47267       ],
47268       [
47269         "Slovakia (Slovensko)",
47270         "sk",
47271         "421"
47272       ],
47273       [
47274         "Slovenia (Slovenija)",
47275         "si",
47276         "386"
47277       ],
47278       [
47279         "Solomon Islands",
47280         "sb",
47281         "677"
47282       ],
47283       [
47284         "Somalia (Soomaaliya)",
47285         "so",
47286         "252"
47287       ],
47288       [
47289         "South Africa",
47290         "za",
47291         "27"
47292       ],
47293       [
47294         "South Korea (대한민국)",
47295         "kr",
47296         "82"
47297       ],
47298       [
47299         "South Sudan (‫جنوب السودان‬‎)",
47300         "ss",
47301         "211"
47302       ],
47303       [
47304         "Spain (España)",
47305         "es",
47306         "34"
47307       ],
47308       [
47309         "Sri Lanka (ශ්‍රී ලංකාව)",
47310         "lk",
47311         "94"
47312       ],
47313       [
47314         "Sudan (‫السودان‬‎)",
47315         "sd",
47316         "249"
47317       ],
47318       [
47319         "Suriname",
47320         "sr",
47321         "597"
47322       ],
47323       [
47324         "Svalbard and Jan Mayen",
47325         "sj",
47326         "47",
47327         1
47328       ],
47329       [
47330         "Swaziland",
47331         "sz",
47332         "268"
47333       ],
47334       [
47335         "Sweden (Sverige)",
47336         "se",
47337         "46"
47338       ],
47339       [
47340         "Switzerland (Schweiz)",
47341         "ch",
47342         "41"
47343       ],
47344       [
47345         "Syria (‫سوريا‬‎)",
47346         "sy",
47347         "963"
47348       ],
47349       [
47350         "Taiwan (台灣)",
47351         "tw",
47352         "886"
47353       ],
47354       [
47355         "Tajikistan",
47356         "tj",
47357         "992"
47358       ],
47359       [
47360         "Tanzania",
47361         "tz",
47362         "255"
47363       ],
47364       [
47365         "Thailand (ไทย)",
47366         "th",
47367         "66"
47368       ],
47369       [
47370         "Timor-Leste",
47371         "tl",
47372         "670"
47373       ],
47374       [
47375         "Togo",
47376         "tg",
47377         "228"
47378       ],
47379       [
47380         "Tokelau",
47381         "tk",
47382         "690"
47383       ],
47384       [
47385         "Tonga",
47386         "to",
47387         "676"
47388       ],
47389       [
47390         "Trinidad and Tobago",
47391         "tt",
47392         "1868"
47393       ],
47394       [
47395         "Tunisia (‫تونس‬‎)",
47396         "tn",
47397         "216"
47398       ],
47399       [
47400         "Turkey (Türkiye)",
47401         "tr",
47402         "90"
47403       ],
47404       [
47405         "Turkmenistan",
47406         "tm",
47407         "993"
47408       ],
47409       [
47410         "Turks and Caicos Islands",
47411         "tc",
47412         "1649"
47413       ],
47414       [
47415         "Tuvalu",
47416         "tv",
47417         "688"
47418       ],
47419       [
47420         "U.S. Virgin Islands",
47421         "vi",
47422         "1340"
47423       ],
47424       [
47425         "Uganda",
47426         "ug",
47427         "256"
47428       ],
47429       [
47430         "Ukraine (Україна)",
47431         "ua",
47432         "380"
47433       ],
47434       [
47435         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
47436         "ae",
47437         "971"
47438       ],
47439       [
47440         "United Kingdom",
47441         "gb",
47442         "44",
47443         0
47444       ],
47445       [
47446         "United States",
47447         "us",
47448         "1",
47449         0
47450       ],
47451       [
47452         "Uruguay",
47453         "uy",
47454         "598"
47455       ],
47456       [
47457         "Uzbekistan (Oʻzbekiston)",
47458         "uz",
47459         "998"
47460       ],
47461       [
47462         "Vanuatu",
47463         "vu",
47464         "678"
47465       ],
47466       [
47467         "Vatican City (Città del Vaticano)",
47468         "va",
47469         "39",
47470         1
47471       ],
47472       [
47473         "Venezuela",
47474         "ve",
47475         "58"
47476       ],
47477       [
47478         "Vietnam (Việt Nam)",
47479         "vn",
47480         "84"
47481       ],
47482       [
47483         "Wallis and Futuna (Wallis-et-Futuna)",
47484         "wf",
47485         "681"
47486       ],
47487       [
47488         "Western Sahara (‫الصحراء الغربية‬‎)",
47489         "eh",
47490         "212",
47491         1
47492       ],
47493       [
47494         "Yemen (‫اليمن‬‎)",
47495         "ye",
47496         "967"
47497       ],
47498       [
47499         "Zambia",
47500         "zm",
47501         "260"
47502       ],
47503       [
47504         "Zimbabwe",
47505         "zw",
47506         "263"
47507       ],
47508       [
47509         "Åland Islands",
47510         "ax",
47511         "358",
47512         1
47513       ]
47514   ];
47515   
47516   return d;
47517 }/**
47518 *    This script refer to:
47519 *    Title: International Telephone Input
47520 *    Author: Jack O'Connor
47521 *    Code version:  v12.1.12
47522 *    Availability: https://github.com/jackocnr/intl-tel-input.git
47523 **/
47524
47525 /**
47526  * @class Roo.bootstrap.form.PhoneInput
47527  * @extends Roo.bootstrap.form.TriggerField
47528  * An input with International dial-code selection
47529  
47530  * @cfg {String} defaultDialCode default '+852'
47531  * @cfg {Array} preferedCountries default []
47532   
47533  * @constructor
47534  * Create a new PhoneInput.
47535  * @param {Object} config Configuration options
47536  */
47537
47538 Roo.bootstrap.form.PhoneInput = function(config) {
47539     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
47540 };
47541
47542 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
47543         /**
47544         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
47545         */
47546         listWidth: undefined,
47547         
47548         selectedClass: 'active',
47549         
47550         invalidClass : "has-warning",
47551         
47552         validClass: 'has-success',
47553         
47554         allowed: '0123456789',
47555         
47556         max_length: 15,
47557         
47558         /**
47559          * @cfg {String} defaultDialCode The default dial code when initializing the input
47560          */
47561         defaultDialCode: '+852',
47562         
47563         /**
47564          * @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
47565          */
47566         preferedCountries: false,
47567         
47568         getAutoCreate : function()
47569         {
47570             var data = Roo.bootstrap.form.PhoneInputData();
47571             var align = this.labelAlign || this.parentLabelAlign();
47572             var id = Roo.id();
47573             
47574             this.allCountries = [];
47575             this.dialCodeMapping = [];
47576             
47577             for (var i = 0; i < data.length; i++) {
47578               var c = data[i];
47579               this.allCountries[i] = {
47580                 name: c[0],
47581                 iso2: c[1],
47582                 dialCode: c[2],
47583                 priority: c[3] || 0,
47584                 areaCodes: c[4] || null
47585               };
47586               this.dialCodeMapping[c[2]] = {
47587                   name: c[0],
47588                   iso2: c[1],
47589                   priority: c[3] || 0,
47590                   areaCodes: c[4] || null
47591               };
47592             }
47593             
47594             var cfg = {
47595                 cls: 'form-group',
47596                 cn: []
47597             };
47598             
47599             var input =  {
47600                 tag: 'input',
47601                 id : id,
47602                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
47603                 maxlength: this.max_length,
47604                 cls : 'form-control tel-input',
47605                 autocomplete: 'new-password'
47606             };
47607             
47608             var hiddenInput = {
47609                 tag: 'input',
47610                 type: 'hidden',
47611                 cls: 'hidden-tel-input'
47612             };
47613             
47614             if (this.name) {
47615                 hiddenInput.name = this.name;
47616             }
47617             
47618             if (this.disabled) {
47619                 input.disabled = true;
47620             }
47621             
47622             var flag_container = {
47623                 tag: 'div',
47624                 cls: 'flag-box',
47625                 cn: [
47626                     {
47627                         tag: 'div',
47628                         cls: 'flag'
47629                     },
47630                     {
47631                         tag: 'div',
47632                         cls: 'caret'
47633                     }
47634                 ]
47635             };
47636             
47637             var box = {
47638                 tag: 'div',
47639                 cls: this.hasFeedback ? 'has-feedback' : '',
47640                 cn: [
47641                     hiddenInput,
47642                     input,
47643                     {
47644                         tag: 'input',
47645                         cls: 'dial-code-holder',
47646                         disabled: true
47647                     }
47648                 ]
47649             };
47650             
47651             var container = {
47652                 cls: 'roo-select2-container input-group',
47653                 cn: [
47654                     flag_container,
47655                     box
47656                 ]
47657             };
47658             
47659             if (this.fieldLabel.length) {
47660                 var indicator = {
47661                     tag: 'i',
47662                     tooltip: 'This field is required'
47663                 };
47664                 
47665                 var label = {
47666                     tag: 'label',
47667                     'for':  id,
47668                     cls: 'control-label',
47669                     cn: []
47670                 };
47671                 
47672                 var label_text = {
47673                     tag: 'span',
47674                     html: this.fieldLabel
47675                 };
47676                 
47677                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
47678                 label.cn = [
47679                     indicator,
47680                     label_text
47681                 ];
47682                 
47683                 if(this.indicatorpos == 'right') {
47684                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
47685                     label.cn = [
47686                         label_text,
47687                         indicator
47688                     ];
47689                 }
47690                 
47691                 if(align == 'left') {
47692                     container = {
47693                         tag: 'div',
47694                         cn: [
47695                             container
47696                         ]
47697                     };
47698                     
47699                     if(this.labelWidth > 12){
47700                         label.style = "width: " + this.labelWidth + 'px';
47701                     }
47702                     if(this.labelWidth < 13 && this.labelmd == 0){
47703                         this.labelmd = this.labelWidth;
47704                     }
47705                     if(this.labellg > 0){
47706                         label.cls += ' col-lg-' + this.labellg;
47707                         input.cls += ' col-lg-' + (12 - this.labellg);
47708                     }
47709                     if(this.labelmd > 0){
47710                         label.cls += ' col-md-' + this.labelmd;
47711                         container.cls += ' col-md-' + (12 - this.labelmd);
47712                     }
47713                     if(this.labelsm > 0){
47714                         label.cls += ' col-sm-' + this.labelsm;
47715                         container.cls += ' col-sm-' + (12 - this.labelsm);
47716                     }
47717                     if(this.labelxs > 0){
47718                         label.cls += ' col-xs-' + this.labelxs;
47719                         container.cls += ' col-xs-' + (12 - this.labelxs);
47720                     }
47721                 }
47722             }
47723             
47724             cfg.cn = [
47725                 label,
47726                 container
47727             ];
47728             
47729             var settings = this;
47730             
47731             ['xs','sm','md','lg'].map(function(size){
47732                 if (settings[size]) {
47733                     cfg.cls += ' col-' + size + '-' + settings[size];
47734                 }
47735             });
47736             
47737             this.store = new Roo.data.Store({
47738                 proxy : new Roo.data.MemoryProxy({}),
47739                 reader : new Roo.data.JsonReader({
47740                     fields : [
47741                         {
47742                             'name' : 'name',
47743                             'type' : 'string'
47744                         },
47745                         {
47746                             'name' : 'iso2',
47747                             'type' : 'string'
47748                         },
47749                         {
47750                             'name' : 'dialCode',
47751                             'type' : 'string'
47752                         },
47753                         {
47754                             'name' : 'priority',
47755                             'type' : 'string'
47756                         },
47757                         {
47758                             'name' : 'areaCodes',
47759                             'type' : 'string'
47760                         }
47761                     ]
47762                 })
47763             });
47764             
47765             if(!this.preferedCountries) {
47766                 this.preferedCountries = [
47767                     'hk',
47768                     'gb',
47769                     'us'
47770                 ];
47771             }
47772             
47773             var p = this.preferedCountries.reverse();
47774             
47775             if(p) {
47776                 for (var i = 0; i < p.length; i++) {
47777                     for (var j = 0; j < this.allCountries.length; j++) {
47778                         if(this.allCountries[j].iso2 == p[i]) {
47779                             var t = this.allCountries[j];
47780                             this.allCountries.splice(j,1);
47781                             this.allCountries.unshift(t);
47782                         }
47783                     } 
47784                 }
47785             }
47786             
47787             this.store.proxy.data = {
47788                 success: true,
47789                 data: this.allCountries
47790             };
47791             
47792             return cfg;
47793         },
47794         
47795         initEvents : function()
47796         {
47797             this.createList();
47798             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
47799             
47800             this.indicator = this.indicatorEl();
47801             this.flag = this.flagEl();
47802             this.dialCodeHolder = this.dialCodeHolderEl();
47803             
47804             this.trigger = this.el.select('div.flag-box',true).first();
47805             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
47806             
47807             var _this = this;
47808             
47809             (function(){
47810                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
47811                 _this.list.setWidth(lw);
47812             }).defer(100);
47813             
47814             this.list.on('mouseover', this.onViewOver, this);
47815             this.list.on('mousemove', this.onViewMove, this);
47816             this.inputEl().on("keyup", this.onKeyUp, this);
47817             this.inputEl().on("keypress", this.onKeyPress, this);
47818             
47819             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
47820
47821             this.view = new Roo.View(this.list, this.tpl, {
47822                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
47823             });
47824             
47825             this.view.on('click', this.onViewClick, this);
47826             this.setValue(this.defaultDialCode);
47827         },
47828         
47829         onTriggerClick : function(e)
47830         {
47831             Roo.log('trigger click');
47832             if(this.disabled){
47833                 return;
47834             }
47835             
47836             if(this.isExpanded()){
47837                 this.collapse();
47838                 this.hasFocus = false;
47839             }else {
47840                 this.store.load({});
47841                 this.hasFocus = true;
47842                 this.expand();
47843             }
47844         },
47845         
47846         isExpanded : function()
47847         {
47848             return this.list.isVisible();
47849         },
47850         
47851         collapse : function()
47852         {
47853             if(!this.isExpanded()){
47854                 return;
47855             }
47856             this.list.hide();
47857             Roo.get(document).un('mousedown', this.collapseIf, this);
47858             Roo.get(document).un('mousewheel', this.collapseIf, this);
47859             this.fireEvent('collapse', this);
47860             this.validate();
47861         },
47862         
47863         expand : function()
47864         {
47865             Roo.log('expand');
47866
47867             if(this.isExpanded() || !this.hasFocus){
47868                 return;
47869             }
47870             
47871             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
47872             this.list.setWidth(lw);
47873             
47874             this.list.show();
47875             this.restrictHeight();
47876             
47877             Roo.get(document).on('mousedown', this.collapseIf, this);
47878             Roo.get(document).on('mousewheel', this.collapseIf, this);
47879             
47880             this.fireEvent('expand', this);
47881         },
47882         
47883         restrictHeight : function()
47884         {
47885             this.list.alignTo(this.inputEl(), this.listAlign);
47886             this.list.alignTo(this.inputEl(), this.listAlign);
47887         },
47888         
47889         onViewOver : function(e, t)
47890         {
47891             if(this.inKeyMode){
47892                 return;
47893             }
47894             var item = this.view.findItemFromChild(t);
47895             
47896             if(item){
47897                 var index = this.view.indexOf(item);
47898                 this.select(index, false);
47899             }
47900         },
47901
47902         // private
47903         onViewClick : function(view, doFocus, el, e)
47904         {
47905             var index = this.view.getSelectedIndexes()[0];
47906             
47907             var r = this.store.getAt(index);
47908             
47909             if(r){
47910                 this.onSelect(r, index);
47911             }
47912             if(doFocus !== false && !this.blockFocus){
47913                 this.inputEl().focus();
47914             }
47915         },
47916         
47917         onViewMove : function(e, t)
47918         {
47919             this.inKeyMode = false;
47920         },
47921         
47922         select : function(index, scrollIntoView)
47923         {
47924             this.selectedIndex = index;
47925             this.view.select(index);
47926             if(scrollIntoView !== false){
47927                 var el = this.view.getNode(index);
47928                 if(el){
47929                     this.list.scrollChildIntoView(el, false);
47930                 }
47931             }
47932         },
47933         
47934         createList : function()
47935         {
47936             this.list = Roo.get(document.body).createChild({
47937                 tag: 'ul',
47938                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
47939                 style: 'display:none'
47940             });
47941             
47942             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
47943         },
47944         
47945         collapseIf : function(e)
47946         {
47947             var in_combo  = e.within(this.el);
47948             var in_list =  e.within(this.list);
47949             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
47950             
47951             if (in_combo || in_list || is_list) {
47952                 return;
47953             }
47954             this.collapse();
47955         },
47956         
47957         onSelect : function(record, index)
47958         {
47959             if(this.fireEvent('beforeselect', this, record, index) !== false){
47960                 
47961                 this.setFlagClass(record.data.iso2);
47962                 this.setDialCode(record.data.dialCode);
47963                 this.hasFocus = false;
47964                 this.collapse();
47965                 this.fireEvent('select', this, record, index);
47966             }
47967         },
47968         
47969         flagEl : function()
47970         {
47971             var flag = this.el.select('div.flag',true).first();
47972             if(!flag){
47973                 return false;
47974             }
47975             return flag;
47976         },
47977         
47978         dialCodeHolderEl : function()
47979         {
47980             var d = this.el.select('input.dial-code-holder',true).first();
47981             if(!d){
47982                 return false;
47983             }
47984             return d;
47985         },
47986         
47987         setDialCode : function(v)
47988         {
47989             this.dialCodeHolder.dom.value = '+'+v;
47990         },
47991         
47992         setFlagClass : function(n)
47993         {
47994             this.flag.dom.className = 'flag '+n;
47995         },
47996         
47997         getValue : function()
47998         {
47999             var v = this.inputEl().getValue();
48000             if(this.dialCodeHolder) {
48001                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
48002             }
48003             return v;
48004         },
48005         
48006         setValue : function(v)
48007         {
48008             var d = this.getDialCode(v);
48009             
48010             //invalid dial code
48011             if(v.length == 0 || !d || d.length == 0) {
48012                 if(this.rendered){
48013                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48014                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48015                 }
48016                 return;
48017             }
48018             
48019             //valid dial code
48020             this.setFlagClass(this.dialCodeMapping[d].iso2);
48021             this.setDialCode(d);
48022             this.inputEl().dom.value = v.replace('+'+d,'');
48023             this.hiddenEl().dom.value = this.getValue();
48024             
48025             this.validate();
48026         },
48027         
48028         getDialCode : function(v)
48029         {
48030             v = v ||  '';
48031             
48032             if (v.length == 0) {
48033                 return this.dialCodeHolder.dom.value;
48034             }
48035             
48036             var dialCode = "";
48037             if (v.charAt(0) != "+") {
48038                 return false;
48039             }
48040             var numericChars = "";
48041             for (var i = 1; i < v.length; i++) {
48042               var c = v.charAt(i);
48043               if (!isNaN(c)) {
48044                 numericChars += c;
48045                 if (this.dialCodeMapping[numericChars]) {
48046                   dialCode = v.substr(1, i);
48047                 }
48048                 if (numericChars.length == 4) {
48049                   break;
48050                 }
48051               }
48052             }
48053             return dialCode;
48054         },
48055         
48056         reset : function()
48057         {
48058             this.setValue(this.defaultDialCode);
48059             this.validate();
48060         },
48061         
48062         hiddenEl : function()
48063         {
48064             return this.el.select('input.hidden-tel-input',true).first();
48065         },
48066         
48067         // after setting val
48068         onKeyUp : function(e){
48069             this.setValue(this.getValue());
48070         },
48071         
48072         onKeyPress : function(e){
48073             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48074                 e.stopEvent();
48075             }
48076         }
48077         
48078 });
48079 /**
48080  * @class Roo.bootstrap.form.MoneyField
48081  * @extends Roo.bootstrap.form.ComboBox
48082  * Bootstrap MoneyField class
48083  * 
48084  * @constructor
48085  * Create a new MoneyField.
48086  * @param {Object} config Configuration options
48087  */
48088
48089 Roo.bootstrap.form.MoneyField = function(config) {
48090     
48091     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48092     
48093 };
48094
48095 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48096     
48097     /**
48098      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48099      */
48100     allowDecimals : true,
48101     /**
48102      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48103      */
48104     decimalSeparator : ".",
48105     /**
48106      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48107      */
48108     decimalPrecision : 0,
48109     /**
48110      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48111      */
48112     allowNegative : true,
48113     /**
48114      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48115      */
48116     allowZero: true,
48117     /**
48118      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48119      */
48120     minValue : Number.NEGATIVE_INFINITY,
48121     /**
48122      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48123      */
48124     maxValue : Number.MAX_VALUE,
48125     /**
48126      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48127      */
48128     minText : "The minimum value for this field is {0}",
48129     /**
48130      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48131      */
48132     maxText : "The maximum value for this field is {0}",
48133     /**
48134      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
48135      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48136      */
48137     nanText : "{0} is not a valid number",
48138     /**
48139      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48140      */
48141     castInt : true,
48142     /**
48143      * @cfg {String} defaults currency of the MoneyField
48144      * value should be in lkey
48145      */
48146     defaultCurrency : false,
48147     /**
48148      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48149      */
48150     thousandsDelimiter : false,
48151     /**
48152      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48153      */
48154     max_length: false,
48155     
48156     inputlg : 9,
48157     inputmd : 9,
48158     inputsm : 9,
48159     inputxs : 6,
48160      /**
48161      * @cfg {Roo.data.Store} store  Store to lookup currency??
48162      */
48163     store : false,
48164     
48165     getAutoCreate : function()
48166     {
48167         var align = this.labelAlign || this.parentLabelAlign();
48168         
48169         var id = Roo.id();
48170
48171         var cfg = {
48172             cls: 'form-group',
48173             cn: []
48174         };
48175
48176         var input =  {
48177             tag: 'input',
48178             id : id,
48179             cls : 'form-control roo-money-amount-input',
48180             autocomplete: 'new-password'
48181         };
48182         
48183         var hiddenInput = {
48184             tag: 'input',
48185             type: 'hidden',
48186             id: Roo.id(),
48187             cls: 'hidden-number-input'
48188         };
48189         
48190         if(this.max_length) {
48191             input.maxlength = this.max_length; 
48192         }
48193         
48194         if (this.name) {
48195             hiddenInput.name = this.name;
48196         }
48197
48198         if (this.disabled) {
48199             input.disabled = true;
48200         }
48201
48202         var clg = 12 - this.inputlg;
48203         var cmd = 12 - this.inputmd;
48204         var csm = 12 - this.inputsm;
48205         var cxs = 12 - this.inputxs;
48206         
48207         var container = {
48208             tag : 'div',
48209             cls : 'row roo-money-field',
48210             cn : [
48211                 {
48212                     tag : 'div',
48213                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
48214                     cn : [
48215                         {
48216                             tag : 'div',
48217                             cls: 'roo-select2-container input-group',
48218                             cn: [
48219                                 {
48220                                     tag : 'input',
48221                                     cls : 'form-control roo-money-currency-input',
48222                                     autocomplete: 'new-password',
48223                                     readOnly : 1,
48224                                     name : this.currencyName
48225                                 },
48226                                 {
48227                                     tag :'span',
48228                                     cls : 'input-group-addon',
48229                                     cn : [
48230                                         {
48231                                             tag: 'span',
48232                                             cls: 'caret'
48233                                         }
48234                                     ]
48235                                 }
48236                             ]
48237                         }
48238                     ]
48239                 },
48240                 {
48241                     tag : 'div',
48242                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
48243                     cn : [
48244                         {
48245                             tag: 'div',
48246                             cls: this.hasFeedback ? 'has-feedback' : '',
48247                             cn: [
48248                                 input
48249                             ]
48250                         }
48251                     ]
48252                 }
48253             ]
48254             
48255         };
48256         
48257         if (this.fieldLabel.length) {
48258             var indicator = {
48259                 tag: 'i',
48260                 tooltip: 'This field is required'
48261             };
48262
48263             var label = {
48264                 tag: 'label',
48265                 'for':  id,
48266                 cls: 'control-label',
48267                 cn: []
48268             };
48269
48270             var label_text = {
48271                 tag: 'span',
48272                 html: this.fieldLabel
48273             };
48274
48275             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48276             label.cn = [
48277                 indicator,
48278                 label_text
48279             ];
48280
48281             if(this.indicatorpos == 'right') {
48282                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48283                 label.cn = [
48284                     label_text,
48285                     indicator
48286                 ];
48287             }
48288
48289             if(align == 'left') {
48290                 container = {
48291                     tag: 'div',
48292                     cn: [
48293                         container
48294                     ]
48295                 };
48296
48297                 if(this.labelWidth > 12){
48298                     label.style = "width: " + this.labelWidth + 'px';
48299                 }
48300                 if(this.labelWidth < 13 && this.labelmd == 0){
48301                     this.labelmd = this.labelWidth;
48302                 }
48303                 if(this.labellg > 0){
48304                     label.cls += ' col-lg-' + this.labellg;
48305                     input.cls += ' col-lg-' + (12 - this.labellg);
48306                 }
48307                 if(this.labelmd > 0){
48308                     label.cls += ' col-md-' + this.labelmd;
48309                     container.cls += ' col-md-' + (12 - this.labelmd);
48310                 }
48311                 if(this.labelsm > 0){
48312                     label.cls += ' col-sm-' + this.labelsm;
48313                     container.cls += ' col-sm-' + (12 - this.labelsm);
48314                 }
48315                 if(this.labelxs > 0){
48316                     label.cls += ' col-xs-' + this.labelxs;
48317                     container.cls += ' col-xs-' + (12 - this.labelxs);
48318                 }
48319             }
48320         }
48321
48322         cfg.cn = [
48323             label,
48324             container,
48325             hiddenInput
48326         ];
48327         
48328         var settings = this;
48329
48330         ['xs','sm','md','lg'].map(function(size){
48331             if (settings[size]) {
48332                 cfg.cls += ' col-' + size + '-' + settings[size];
48333             }
48334         });
48335         
48336         return cfg;
48337     },
48338     
48339     initEvents : function()
48340     {
48341         this.indicator = this.indicatorEl();
48342         
48343         this.initCurrencyEvent();
48344         
48345         this.initNumberEvent();
48346     },
48347     
48348     initCurrencyEvent : function()
48349     {
48350         if (!this.store) {
48351             throw "can not find store for combo";
48352         }
48353         
48354         this.store = Roo.factory(this.store, Roo.data);
48355         this.store.parent = this;
48356         
48357         this.createList();
48358         
48359         this.triggerEl = this.el.select('.input-group-addon', true).first();
48360         
48361         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
48362         
48363         var _this = this;
48364         
48365         (function(){
48366             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48367             _this.list.setWidth(lw);
48368         }).defer(100);
48369         
48370         this.list.on('mouseover', this.onViewOver, this);
48371         this.list.on('mousemove', this.onViewMove, this);
48372         this.list.on('scroll', this.onViewScroll, this);
48373         
48374         if(!this.tpl){
48375             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
48376         }
48377         
48378         this.view = new Roo.View(this.list, this.tpl, {
48379             singleSelect:true, store: this.store, selectedClass: this.selectedClass
48380         });
48381         
48382         this.view.on('click', this.onViewClick, this);
48383         
48384         this.store.on('beforeload', this.onBeforeLoad, this);
48385         this.store.on('load', this.onLoad, this);
48386         this.store.on('loadexception', this.onLoadException, this);
48387         
48388         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
48389             "up" : function(e){
48390                 this.inKeyMode = true;
48391                 this.selectPrev();
48392             },
48393
48394             "down" : function(e){
48395                 if(!this.isExpanded()){
48396                     this.onTriggerClick();
48397                 }else{
48398                     this.inKeyMode = true;
48399                     this.selectNext();
48400                 }
48401             },
48402
48403             "enter" : function(e){
48404                 this.collapse();
48405                 
48406                 if(this.fireEvent("specialkey", this, e)){
48407                     this.onViewClick(false);
48408                 }
48409                 
48410                 return true;
48411             },
48412
48413             "esc" : function(e){
48414                 this.collapse();
48415             },
48416
48417             "tab" : function(e){
48418                 this.collapse();
48419                 
48420                 if(this.fireEvent("specialkey", this, e)){
48421                     this.onViewClick(false);
48422                 }
48423                 
48424                 return true;
48425             },
48426
48427             scope : this,
48428
48429             doRelay : function(foo, bar, hname){
48430                 if(hname == 'down' || this.scope.isExpanded()){
48431                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48432                 }
48433                 return true;
48434             },
48435
48436             forceKeyDown: true
48437         });
48438         
48439         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
48440         
48441     },
48442     
48443     initNumberEvent : function(e)
48444     {
48445         this.inputEl().on("keydown" , this.fireKey,  this);
48446         this.inputEl().on("focus", this.onFocus,  this);
48447         this.inputEl().on("blur", this.onBlur,  this);
48448         
48449         this.inputEl().relayEvent('keyup', this);
48450         
48451         if(this.indicator){
48452             this.indicator.addClass('invisible');
48453         }
48454  
48455         this.originalValue = this.getValue();
48456         
48457         if(this.validationEvent == 'keyup'){
48458             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
48459             this.inputEl().on('keyup', this.filterValidation, this);
48460         }
48461         else if(this.validationEvent !== false){
48462             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
48463         }
48464         
48465         if(this.selectOnFocus){
48466             this.on("focus", this.preFocus, this);
48467             
48468         }
48469         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
48470             this.inputEl().on("keypress", this.filterKeys, this);
48471         } else {
48472             this.inputEl().relayEvent('keypress', this);
48473         }
48474         
48475         var allowed = "0123456789";
48476         
48477         if(this.allowDecimals){
48478             allowed += this.decimalSeparator;
48479         }
48480         
48481         if(this.allowNegative){
48482             allowed += "-";
48483         }
48484         
48485         if(this.thousandsDelimiter) {
48486             allowed += ",";
48487         }
48488         
48489         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
48490         
48491         var keyPress = function(e){
48492             
48493             var k = e.getKey();
48494             
48495             var c = e.getCharCode();
48496             
48497             if(
48498                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
48499                     allowed.indexOf(String.fromCharCode(c)) === -1
48500             ){
48501                 e.stopEvent();
48502                 return;
48503             }
48504             
48505             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
48506                 return;
48507             }
48508             
48509             if(allowed.indexOf(String.fromCharCode(c)) === -1){
48510                 e.stopEvent();
48511             }
48512         };
48513         
48514         this.inputEl().on("keypress", keyPress, this);
48515         
48516     },
48517     
48518     onTriggerClick : function(e)
48519     {   
48520         if(this.disabled){
48521             return;
48522         }
48523         
48524         this.page = 0;
48525         this.loadNext = false;
48526         
48527         if(this.isExpanded()){
48528             this.collapse();
48529             return;
48530         }
48531         
48532         this.hasFocus = true;
48533         
48534         if(this.triggerAction == 'all') {
48535             this.doQuery(this.allQuery, true);
48536             return;
48537         }
48538         
48539         this.doQuery(this.getRawValue());
48540     },
48541     
48542     getCurrency : function()
48543     {   
48544         var v = this.currencyEl().getValue();
48545         
48546         return v;
48547     },
48548     
48549     restrictHeight : function()
48550     {
48551         this.list.alignTo(this.currencyEl(), this.listAlign);
48552         this.list.alignTo(this.currencyEl(), this.listAlign);
48553     },
48554     
48555     onViewClick : function(view, doFocus, el, e)
48556     {
48557         var index = this.view.getSelectedIndexes()[0];
48558         
48559         var r = this.store.getAt(index);
48560         
48561         if(r){
48562             this.onSelect(r, index);
48563         }
48564     },
48565     
48566     onSelect : function(record, index){
48567         
48568         if(this.fireEvent('beforeselect', this, record, index) !== false){
48569         
48570             this.setFromCurrencyData(index > -1 ? record.data : false);
48571             
48572             this.collapse();
48573             
48574             this.fireEvent('select', this, record, index);
48575         }
48576     },
48577     
48578     setFromCurrencyData : function(o)
48579     {
48580         var currency = '';
48581         
48582         this.lastCurrency = o;
48583         
48584         if (this.currencyField) {
48585             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
48586         } else {
48587             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
48588         }
48589         
48590         this.lastSelectionText = currency;
48591         
48592         //setting default currency
48593         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
48594             this.setCurrency(this.defaultCurrency);
48595             return;
48596         }
48597         
48598         this.setCurrency(currency);
48599     },
48600     
48601     setFromData : function(o)
48602     {
48603         var c = {};
48604         
48605         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
48606         
48607         this.setFromCurrencyData(c);
48608         
48609         var value = '';
48610         
48611         if (this.name) {
48612             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
48613         } else {
48614             Roo.log('no value set for '+ (this.name ? this.name : this.id));
48615         }
48616         
48617         this.setValue(value);
48618         
48619     },
48620     
48621     setCurrency : function(v)
48622     {   
48623         this.currencyValue = v;
48624         
48625         if(this.rendered){
48626             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
48627             this.validate();
48628         }
48629     },
48630     
48631     setValue : function(v)
48632     {
48633         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
48634         
48635         this.value = v;
48636         
48637         if(this.rendered){
48638             
48639             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48640             
48641             this.inputEl().dom.value = (v == '') ? '' :
48642                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
48643             
48644             if(!this.allowZero && v === '0') {
48645                 this.hiddenEl().dom.value = '';
48646                 this.inputEl().dom.value = '';
48647             }
48648             
48649             this.validate();
48650         }
48651     },
48652     
48653     getRawValue : function()
48654     {
48655         var v = this.inputEl().getValue();
48656         
48657         return v;
48658     },
48659     
48660     getValue : function()
48661     {
48662         return this.fixPrecision(this.parseValue(this.getRawValue()));
48663     },
48664     
48665     parseValue : function(value)
48666     {
48667         if(this.thousandsDelimiter) {
48668             value += "";
48669             r = new RegExp(",", "g");
48670             value = value.replace(r, "");
48671         }
48672         
48673         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
48674         return isNaN(value) ? '' : value;
48675         
48676     },
48677     
48678     fixPrecision : function(value)
48679     {
48680         if(this.thousandsDelimiter) {
48681             value += "";
48682             r = new RegExp(",", "g");
48683             value = value.replace(r, "");
48684         }
48685         
48686         var nan = isNaN(value);
48687         
48688         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
48689             return nan ? '' : value;
48690         }
48691         return parseFloat(value).toFixed(this.decimalPrecision);
48692     },
48693     
48694     decimalPrecisionFcn : function(v)
48695     {
48696         return Math.floor(v);
48697     },
48698     
48699     validateValue : function(value)
48700     {
48701         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
48702             return false;
48703         }
48704         
48705         var num = this.parseValue(value);
48706         
48707         if(isNaN(num)){
48708             this.markInvalid(String.format(this.nanText, value));
48709             return false;
48710         }
48711         
48712         if(num < this.minValue){
48713             this.markInvalid(String.format(this.minText, this.minValue));
48714             return false;
48715         }
48716         
48717         if(num > this.maxValue){
48718             this.markInvalid(String.format(this.maxText, this.maxValue));
48719             return false;
48720         }
48721         
48722         return true;
48723     },
48724     
48725     validate : function()
48726     {
48727         if(this.disabled || this.allowBlank){
48728             this.markValid();
48729             return true;
48730         }
48731         
48732         var currency = this.getCurrency();
48733         
48734         if(this.validateValue(this.getRawValue()) && currency.length){
48735             this.markValid();
48736             return true;
48737         }
48738         
48739         this.markInvalid();
48740         return false;
48741     },
48742     
48743     getName: function()
48744     {
48745         return this.name;
48746     },
48747     
48748     beforeBlur : function()
48749     {
48750         if(!this.castInt){
48751             return;
48752         }
48753         
48754         var v = this.parseValue(this.getRawValue());
48755         
48756         if(v || v == 0){
48757             this.setValue(v);
48758         }
48759     },
48760     
48761     onBlur : function()
48762     {
48763         this.beforeBlur();
48764         
48765         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
48766             //this.el.removeClass(this.focusClass);
48767         }
48768         
48769         this.hasFocus = false;
48770         
48771         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
48772             this.validate();
48773         }
48774         
48775         var v = this.getValue();
48776         
48777         if(String(v) !== String(this.startValue)){
48778             this.fireEvent('change', this, v, this.startValue);
48779         }
48780         
48781         this.fireEvent("blur", this);
48782     },
48783     
48784     inputEl : function()
48785     {
48786         return this.el.select('.roo-money-amount-input', true).first();
48787     },
48788     
48789     currencyEl : function()
48790     {
48791         return this.el.select('.roo-money-currency-input', true).first();
48792     },
48793     
48794     hiddenEl : function()
48795     {
48796         return this.el.select('input.hidden-number-input',true).first();
48797     }
48798     
48799 });/**
48800  * @class Roo.bootstrap.BezierSignature
48801  * @extends Roo.bootstrap.Component
48802  * Bootstrap BezierSignature class
48803  * This script refer to:
48804  *    Title: Signature Pad
48805  *    Author: szimek
48806  *    Availability: https://github.com/szimek/signature_pad
48807  *
48808  * @constructor
48809  * Create a new BezierSignature
48810  * @param {Object} config The config object
48811  */
48812
48813 Roo.bootstrap.BezierSignature = function(config){
48814     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
48815     this.addEvents({
48816         "resize" : true
48817     });
48818 };
48819
48820 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
48821 {
48822      
48823     curve_data: [],
48824     
48825     is_empty: true,
48826     
48827     mouse_btn_down: true,
48828     
48829     /**
48830      * @cfg {int} canvas height
48831      */
48832     canvas_height: '200px',
48833     
48834     /**
48835      * @cfg {float|function} Radius of a single dot.
48836      */ 
48837     dot_size: false,
48838     
48839     /**
48840      * @cfg {float} Minimum width of a line. Defaults to 0.5.
48841      */
48842     min_width: 0.5,
48843     
48844     /**
48845      * @cfg {float} Maximum width of a line. Defaults to 2.5.
48846      */
48847     max_width: 2.5,
48848     
48849     /**
48850      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
48851      */
48852     throttle: 16,
48853     
48854     /**
48855      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
48856      */
48857     min_distance: 5,
48858     
48859     /**
48860      * @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.
48861      */
48862     bg_color: 'rgba(0, 0, 0, 0)',
48863     
48864     /**
48865      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
48866      */
48867     dot_color: 'black',
48868     
48869     /**
48870      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
48871      */ 
48872     velocity_filter_weight: 0.7,
48873     
48874     /**
48875      * @cfg {function} Callback when stroke begin. 
48876      */
48877     onBegin: false,
48878     
48879     /**
48880      * @cfg {function} Callback when stroke end.
48881      */
48882     onEnd: false,
48883     
48884     getAutoCreate : function()
48885     {
48886         var cls = 'roo-signature column';
48887         
48888         if(this.cls){
48889             cls += ' ' + this.cls;
48890         }
48891         
48892         var col_sizes = [
48893             'lg',
48894             'md',
48895             'sm',
48896             'xs'
48897         ];
48898         
48899         for(var i = 0; i < col_sizes.length; i++) {
48900             if(this[col_sizes[i]]) {
48901                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
48902             }
48903         }
48904         
48905         var cfg = {
48906             tag: 'div',
48907             cls: cls,
48908             cn: [
48909                 {
48910                     tag: 'div',
48911                     cls: 'roo-signature-body',
48912                     cn: [
48913                         {
48914                             tag: 'canvas',
48915                             cls: 'roo-signature-body-canvas',
48916                             height: this.canvas_height,
48917                             width: this.canvas_width
48918                         }
48919                     ]
48920                 },
48921                 {
48922                     tag: 'input',
48923                     type: 'file',
48924                     style: 'display: none'
48925                 }
48926             ]
48927         };
48928         
48929         return cfg;
48930     },
48931     
48932     initEvents: function() 
48933     {
48934         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
48935         
48936         var canvas = this.canvasEl();
48937         
48938         // mouse && touch event swapping...
48939         canvas.dom.style.touchAction = 'none';
48940         canvas.dom.style.msTouchAction = 'none';
48941         
48942         this.mouse_btn_down = false;
48943         canvas.on('mousedown', this._handleMouseDown, this);
48944         canvas.on('mousemove', this._handleMouseMove, this);
48945         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
48946         
48947         if (window.PointerEvent) {
48948             canvas.on('pointerdown', this._handleMouseDown, this);
48949             canvas.on('pointermove', this._handleMouseMove, this);
48950             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
48951         }
48952         
48953         if ('ontouchstart' in window) {
48954             canvas.on('touchstart', this._handleTouchStart, this);
48955             canvas.on('touchmove', this._handleTouchMove, this);
48956             canvas.on('touchend', this._handleTouchEnd, this);
48957         }
48958         
48959         Roo.EventManager.onWindowResize(this.resize, this, true);
48960         
48961         // file input event
48962         this.fileEl().on('change', this.uploadImage, this);
48963         
48964         this.clear();
48965         
48966         this.resize();
48967     },
48968     
48969     resize: function(){
48970         
48971         var canvas = this.canvasEl().dom;
48972         var ctx = this.canvasElCtx();
48973         var img_data = false;
48974         
48975         if(canvas.width > 0) {
48976             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
48977         }
48978         // setting canvas width will clean img data
48979         canvas.width = 0;
48980         
48981         var style = window.getComputedStyle ? 
48982             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
48983             
48984         var padding_left = parseInt(style.paddingLeft) || 0;
48985         var padding_right = parseInt(style.paddingRight) || 0;
48986         
48987         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
48988         
48989         if(img_data) {
48990             ctx.putImageData(img_data, 0, 0);
48991         }
48992     },
48993     
48994     _handleMouseDown: function(e)
48995     {
48996         if (e.browserEvent.which === 1) {
48997             this.mouse_btn_down = true;
48998             this.strokeBegin(e);
48999         }
49000     },
49001     
49002     _handleMouseMove: function (e)
49003     {
49004         if (this.mouse_btn_down) {
49005             this.strokeMoveUpdate(e);
49006         }
49007     },
49008     
49009     _handleMouseUp: function (e)
49010     {
49011         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49012             this.mouse_btn_down = false;
49013             this.strokeEnd(e);
49014         }
49015     },
49016     
49017     _handleTouchStart: function (e) {
49018         
49019         e.preventDefault();
49020         if (e.browserEvent.targetTouches.length === 1) {
49021             // var touch = e.browserEvent.changedTouches[0];
49022             // this.strokeBegin(touch);
49023             
49024              this.strokeBegin(e); // assume e catching the correct xy...
49025         }
49026     },
49027     
49028     _handleTouchMove: function (e) {
49029         e.preventDefault();
49030         // var touch = event.targetTouches[0];
49031         // _this._strokeMoveUpdate(touch);
49032         this.strokeMoveUpdate(e);
49033     },
49034     
49035     _handleTouchEnd: function (e) {
49036         var wasCanvasTouched = e.target === this.canvasEl().dom;
49037         if (wasCanvasTouched) {
49038             e.preventDefault();
49039             // var touch = event.changedTouches[0];
49040             // _this._strokeEnd(touch);
49041             this.strokeEnd(e);
49042         }
49043     },
49044     
49045     reset: function () {
49046         this._lastPoints = [];
49047         this._lastVelocity = 0;
49048         this._lastWidth = (this.min_width + this.max_width) / 2;
49049         this.canvasElCtx().fillStyle = this.dot_color;
49050     },
49051     
49052     strokeMoveUpdate: function(e)
49053     {
49054         this.strokeUpdate(e);
49055         
49056         if (this.throttle) {
49057             this.throttleStroke(this.strokeUpdate, this.throttle);
49058         }
49059         else {
49060             this.strokeUpdate(e);
49061         }
49062     },
49063     
49064     strokeBegin: function(e)
49065     {
49066         var newPointGroup = {
49067             color: this.dot_color,
49068             points: []
49069         };
49070         
49071         if (typeof this.onBegin === 'function') {
49072             this.onBegin(e);
49073         }
49074         
49075         this.curve_data.push(newPointGroup);
49076         this.reset();
49077         this.strokeUpdate(e);
49078     },
49079     
49080     strokeUpdate: function(e)
49081     {
49082         var rect = this.canvasEl().dom.getBoundingClientRect();
49083         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49084         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49085         var lastPoints = lastPointGroup.points;
49086         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49087         var isLastPointTooClose = lastPoint
49088             ? point.distanceTo(lastPoint) <= this.min_distance
49089             : false;
49090         var color = lastPointGroup.color;
49091         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49092             var curve = this.addPoint(point);
49093             if (!lastPoint) {
49094                 this.drawDot({color: color, point: point});
49095             }
49096             else if (curve) {
49097                 this.drawCurve({color: color, curve: curve});
49098             }
49099             lastPoints.push({
49100                 time: point.time,
49101                 x: point.x,
49102                 y: point.y
49103             });
49104         }
49105     },
49106     
49107     strokeEnd: function(e)
49108     {
49109         this.strokeUpdate(e);
49110         if (typeof this.onEnd === 'function') {
49111             this.onEnd(e);
49112         }
49113     },
49114     
49115     addPoint:  function (point) {
49116         var _lastPoints = this._lastPoints;
49117         _lastPoints.push(point);
49118         if (_lastPoints.length > 2) {
49119             if (_lastPoints.length === 3) {
49120                 _lastPoints.unshift(_lastPoints[0]);
49121             }
49122             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49123             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49124             _lastPoints.shift();
49125             return curve;
49126         }
49127         return null;
49128     },
49129     
49130     calculateCurveWidths: function (startPoint, endPoint) {
49131         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49132             (1 - this.velocity_filter_weight) * this._lastVelocity;
49133
49134         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49135         var widths = {
49136             end: newWidth,
49137             start: this._lastWidth
49138         };
49139         
49140         this._lastVelocity = velocity;
49141         this._lastWidth = newWidth;
49142         return widths;
49143     },
49144     
49145     drawDot: function (_a) {
49146         var color = _a.color, point = _a.point;
49147         var ctx = this.canvasElCtx();
49148         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49149         ctx.beginPath();
49150         this.drawCurveSegment(point.x, point.y, width);
49151         ctx.closePath();
49152         ctx.fillStyle = color;
49153         ctx.fill();
49154     },
49155     
49156     drawCurve: function (_a) {
49157         var color = _a.color, curve = _a.curve;
49158         var ctx = this.canvasElCtx();
49159         var widthDelta = curve.endWidth - curve.startWidth;
49160         var drawSteps = Math.floor(curve.length()) * 2;
49161         ctx.beginPath();
49162         ctx.fillStyle = color;
49163         for (var i = 0; i < drawSteps; i += 1) {
49164         var t = i / drawSteps;
49165         var tt = t * t;
49166         var ttt = tt * t;
49167         var u = 1 - t;
49168         var uu = u * u;
49169         var uuu = uu * u;
49170         var x = uuu * curve.startPoint.x;
49171         x += 3 * uu * t * curve.control1.x;
49172         x += 3 * u * tt * curve.control2.x;
49173         x += ttt * curve.endPoint.x;
49174         var y = uuu * curve.startPoint.y;
49175         y += 3 * uu * t * curve.control1.y;
49176         y += 3 * u * tt * curve.control2.y;
49177         y += ttt * curve.endPoint.y;
49178         var width = curve.startWidth + ttt * widthDelta;
49179         this.drawCurveSegment(x, y, width);
49180         }
49181         ctx.closePath();
49182         ctx.fill();
49183     },
49184     
49185     drawCurveSegment: function (x, y, width) {
49186         var ctx = this.canvasElCtx();
49187         ctx.moveTo(x, y);
49188         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
49189         this.is_empty = false;
49190     },
49191     
49192     clear: function()
49193     {
49194         var ctx = this.canvasElCtx();
49195         var canvas = this.canvasEl().dom;
49196         ctx.fillStyle = this.bg_color;
49197         ctx.clearRect(0, 0, canvas.width, canvas.height);
49198         ctx.fillRect(0, 0, canvas.width, canvas.height);
49199         this.curve_data = [];
49200         this.reset();
49201         this.is_empty = true;
49202     },
49203     
49204     fileEl: function()
49205     {
49206         return  this.el.select('input',true).first();
49207     },
49208     
49209     canvasEl: function()
49210     {
49211         return this.el.select('canvas',true).first();
49212     },
49213     
49214     canvasElCtx: function()
49215     {
49216         return this.el.select('canvas',true).first().dom.getContext('2d');
49217     },
49218     
49219     getImage: function(type)
49220     {
49221         if(this.is_empty) {
49222             return false;
49223         }
49224         
49225         // encryption ?
49226         return this.canvasEl().dom.toDataURL('image/'+type, 1);
49227     },
49228     
49229     drawFromImage: function(img_src)
49230     {
49231         var img = new Image();
49232         
49233         img.onload = function(){
49234             this.canvasElCtx().drawImage(img, 0, 0);
49235         }.bind(this);
49236         
49237         img.src = img_src;
49238         
49239         this.is_empty = false;
49240     },
49241     
49242     selectImage: function()
49243     {
49244         this.fileEl().dom.click();
49245     },
49246     
49247     uploadImage: function(e)
49248     {
49249         var reader = new FileReader();
49250         
49251         reader.onload = function(e){
49252             var img = new Image();
49253             img.onload = function(){
49254                 this.reset();
49255                 this.canvasElCtx().drawImage(img, 0, 0);
49256             }.bind(this);
49257             img.src = e.target.result;
49258         }.bind(this);
49259         
49260         reader.readAsDataURL(e.target.files[0]);
49261     },
49262     
49263     // Bezier Point Constructor
49264     Point: (function () {
49265         function Point(x, y, time) {
49266             this.x = x;
49267             this.y = y;
49268             this.time = time || Date.now();
49269         }
49270         Point.prototype.distanceTo = function (start) {
49271             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
49272         };
49273         Point.prototype.equals = function (other) {
49274             return this.x === other.x && this.y === other.y && this.time === other.time;
49275         };
49276         Point.prototype.velocityFrom = function (start) {
49277             return this.time !== start.time
49278             ? this.distanceTo(start) / (this.time - start.time)
49279             : 0;
49280         };
49281         return Point;
49282     }()),
49283     
49284     
49285     // Bezier Constructor
49286     Bezier: (function () {
49287         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
49288             this.startPoint = startPoint;
49289             this.control2 = control2;
49290             this.control1 = control1;
49291             this.endPoint = endPoint;
49292             this.startWidth = startWidth;
49293             this.endWidth = endWidth;
49294         }
49295         Bezier.fromPoints = function (points, widths, scope) {
49296             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
49297             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
49298             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
49299         };
49300         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
49301             var dx1 = s1.x - s2.x;
49302             var dy1 = s1.y - s2.y;
49303             var dx2 = s2.x - s3.x;
49304             var dy2 = s2.y - s3.y;
49305             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
49306             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
49307             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
49308             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
49309             var dxm = m1.x - m2.x;
49310             var dym = m1.y - m2.y;
49311             var k = l2 / (l1 + l2);
49312             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
49313             var tx = s2.x - cm.x;
49314             var ty = s2.y - cm.y;
49315             return {
49316                 c1: new scope.Point(m1.x + tx, m1.y + ty),
49317                 c2: new scope.Point(m2.x + tx, m2.y + ty)
49318             };
49319         };
49320         Bezier.prototype.length = function () {
49321             var steps = 10;
49322             var length = 0;
49323             var px;
49324             var py;
49325             for (var i = 0; i <= steps; i += 1) {
49326                 var t = i / steps;
49327                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
49328                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
49329                 if (i > 0) {
49330                     var xdiff = cx - px;
49331                     var ydiff = cy - py;
49332                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
49333                 }
49334                 px = cx;
49335                 py = cy;
49336             }
49337             return length;
49338         };
49339         Bezier.prototype.point = function (t, start, c1, c2, end) {
49340             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
49341             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
49342             + (3.0 * c2 * (1.0 - t) * t * t)
49343             + (end * t * t * t);
49344         };
49345         return Bezier;
49346     }()),
49347     
49348     throttleStroke: function(fn, wait) {
49349       if (wait === void 0) { wait = 250; }
49350       var previous = 0;
49351       var timeout = null;
49352       var result;
49353       var storedContext;
49354       var storedArgs;
49355       var later = function () {
49356           previous = Date.now();
49357           timeout = null;
49358           result = fn.apply(storedContext, storedArgs);
49359           if (!timeout) {
49360               storedContext = null;
49361               storedArgs = [];
49362           }
49363       };
49364       return function wrapper() {
49365           var args = [];
49366           for (var _i = 0; _i < arguments.length; _i++) {
49367               args[_i] = arguments[_i];
49368           }
49369           var now = Date.now();
49370           var remaining = wait - (now - previous);
49371           storedContext = this;
49372           storedArgs = args;
49373           if (remaining <= 0 || remaining > wait) {
49374               if (timeout) {
49375                   clearTimeout(timeout);
49376                   timeout = null;
49377               }
49378               previous = now;
49379               result = fn.apply(storedContext, storedArgs);
49380               if (!timeout) {
49381                   storedContext = null;
49382                   storedArgs = [];
49383               }
49384           }
49385           else if (!timeout) {
49386               timeout = window.setTimeout(later, remaining);
49387           }
49388           return result;
49389       };
49390   }
49391   
49392 });
49393
49394  
49395
49396  // old names for form elements
49397 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
49398 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
49399 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
49400 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
49401 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
49402 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
49403 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
49404 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
49405 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
49406 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
49407 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
49408 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
49409 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
49410 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
49411 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
49412 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
49413 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
49414 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
49415 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
49416 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
49417 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
49418 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
49419 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
49420 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
49421 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
49422 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
49423
49424 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
49425 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
49426
49427 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
49428 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
49429
49430 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
49431 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
49432 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
49433 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
49434